From 010315ceae7efbcf7a7b345343adfd551be91873 Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Mon, 9 Feb 2026 17:21:24 -0500 Subject: [PATCH 1/9] Add CLAUDE.md and ONBOARDING.md for developer onboarding CLAUDE.md provides guidance for Claude Code with build commands, architecture overview, and conventions. ONBOARDING.md is a progressive tutorial that takes Ethereum developers from reading code to deploying on devnet. README.md updated with a link to the onboarding guide. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 119 +++++++ ONBOARDING.md | 963 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 3 files changed, 1084 insertions(+) create mode 100644 CLAUDE.md create mode 100644 ONBOARDING.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6848091 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,119 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Is + +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. + +**Aztec version pinned:** `3.0.0-devnet.6-patch.1` (check `Nargo.toml` and `package.json` for source of truth). + +## Commands + +### Build +```bash +yarn compile # Compile Noir contract (runs `aztec compile`) -> ./target/ +yarn codegen # Generate TypeScript bindings -> ./src/artifacts/PodRacing.ts +yarn compile && yarn codegen # Full rebuild (always run both after contract changes) +``` + +### Test +```bash +yarn test # Run ALL tests (Noir unit + TypeScript E2E) +yarn test:nr # Noir unit tests only (no network needed, uses TXE) +yarn test:js # TypeScript E2E tests only (requires local network running) +``` + +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`). + +### Local Network (required for `test:js`, scripts, and deploy) +```bash +aztec start --local-network # Start Aztec node + PXE + Anvil L1 +rm -rf ./store # MUST delete after restarting local network +``` + +### Scripts (all use `node --loader ts-node/esm`) +```bash +yarn deploy # Deploy account + Pod Racing contract +yarn deploy-account # Deploy a Schnorr account only +yarn interaction-existing-contract # Interact with deployed contract (needs .env vars) +yarn multiple-wallet # Multi-PXE demo +yarn fees # Fee payment methods demo +yarn profile # Transaction profiling +yarn get-block # Query block data +``` + +### Devnet +Append `::devnet` to any script to target devnet (sets `AZTEC_ENV=devnet`): +```bash +yarn deploy::devnet +yarn test::devnet +``` + +### Clean +```bash +yarn clean # Delete ./src/artifacts and ./target +yarn clear-store # Delete ./store (PXE data) +``` + +## Architecture + +### Two Languages, One Contract +- **Noir** (`.nr` files in `src/`) — the smart contract, compiled to ZK circuits +- **TypeScript** (`.ts` files in `scripts/`, `src/test/e2e/`, `src/utils/`) — deployment scripts, E2E tests, and utilities + +### Contract Structure (`src/`) +- `main.nr` — Pod Racing contract entry point. Contains all public/private functions. Imports `mod test`, `mod game_round_note`, `mod race`. +- `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()`. +- `game_round_note.nr` — `GameRoundNote` private note storing one round's point allocation. The `#[note]` macro makes it a private state primitive. + +### Key Aztec Pattern: Private-to-Public Flow +The contract demonstrates the core Aztec pattern where private functions enqueue public calls: +1. `play_round` (private) — creates encrypted `GameRoundNote`, then enqueues `validate_and_play_round` (public) to update round counter +2. `finish_game` (private) — reads player's private notes, sums totals, then enqueues `validate_finish_game_and_reveal` (public) to publish scores + +Functions marked `#[only_self]` can only be called by the contract itself via `self.enqueue(...)`. + +### TypeScript Side +- `config/config.ts` — Singleton `ConfigManager` that loads `config/local-network.json` or `config/devnet.json` based on `AZTEC_ENV`. Exports: `getAztecNodeUrl()`, `getTimeouts()`, `getEnv()`. +- `src/utils/setup_wallet.ts` — Creates `TestWallet` connected to the Aztec node. Enables prover on non-local environments. +- `src/utils/sponsored_fpc.ts` — Gets the canonical `SponsoredFPC` instance (salt=0) for sponsored fee payment. +- `src/utils/deploy_account.ts` — Deploys a Schnorr account with random keys using sponsored fees. +- `src/utils/create_account_from_env.ts` — Recreates an account from `SECRET`, `SIGNING_KEY`, `SALT` env vars. +- `src/artifacts/PodRacing.ts` — **Generated file, do not edit.** TypeScript bindings for the contract. + +### Test Structure +**Noir tests** (`src/test/`): Use TXE (Testing eXecution Environment), no network needed. +- `utils.nr` — `setup()` deploys contract, returns `(TestEnvironment, contract_address, admin)` +- `helpers.nr` — Reusable allocation strategies and game setup helpers +- `pod_racing.nr` — Test cases. Uses `#[test]` and `#[test(should_fail)]` attributes. + +**TypeScript E2E tests** (`src/test/e2e/`): Use Jest, require local network. +- `index.test.ts` — Pod Racing game lifecycle tests +- `accounts.test.ts` — Account deployment and fee payment tests + +### Environment Variables (`.env`) +See `.env.example` for format. Key vars: +- `SECRET`, `SIGNING_KEY`, `SALT` — Account credentials +- `AZTEC_ENV` — `local-network` (default) or `devnet` +- `POD_RACING_CONTRACT_ADDRESS`, `CONTRACT_SALT`, `CONTRACT_DEPLOYER`, `CONTRACT_CONSTRUCTOR_ARGS` — For interacting with existing contracts + +## Important Conventions + +- **Node.js v22.15.0** required +- **Yarn 1.x** as package manager +- **ESM modules** — package.json has `"type": "module"`, tsconfig uses `NodeNext` module resolution +- **4-space indentation** in TypeScript +- **Do not commit** `src/artifacts/`, `target/`, `store/`, or `.env` +- After restarting the local network, always delete `./store` to avoid stale PXE data +- All transactions use `SponsoredFeePaymentMethod` for simplicity — register the FPC with `wallet.registerContract(sponsoredFPC, SponsoredFPCContract.artifact)` before use +- When modifying the contract, always run `yarn compile && yarn codegen` before testing TypeScript + +## ONBOARDING.md Maintenance + +This repo includes an `ONBOARDING.md` that serves as a progressive tutorial for Ethereum developers learning Aztec. **When making code changes, check if `ONBOARDING.md` references the changed code** (line numbers, function signatures, code snippets, struct definitions) and update it accordingly. Key sections that embed code: +- Phase 1 (1.3-1.6): Contract storage, public/private functions, game flow table +- Phase 2 (2.3): Noir test patterns and helpers +- Phase 5 (5.1-5.3): Guided exercises with code examples +- Appendix A: File map table +- Appendix B: Commands table diff --git a/ONBOARDING.md b/ONBOARDING.md new file mode 100644 index 0000000..a70a768 --- /dev/null +++ b/ONBOARDING.md @@ -0,0 +1,963 @@ +# Onboarding: From Ethereum to Aztec + +This guide takes you from "reading code in a browser" to "deploying on devnet" — progressively, with no install required until Phase 3. + +**What you'll learn:** How Aztec contracts work by studying a Pod Racing game — a two-player competitive game that uses private state to implement commit-reveal in a single transaction. + +**How the guide is structured:** + +- **Phases 1-2** need only a browser (read code, compile in a Codespace) +- **Phases 3-6** need local tools (deploy, interact, extend, advanced topics) + +**Aztec version pinned in this repo:** `3.0.0-devnet.6-patch.1` + +**Links:** + +- [Aztec Docs](https://docs.aztec.network) +- [Noir Language](https://noir-lang.org) +- [Discord](https://discord.gg/aztec) + +--- + +## Phase 1: Read and Understand (No Install Required) + +**Goal:** Build a mental model of Aztec by reading the contract code in your browser. + +### 1.1 — Aztec vs Ethereum: Quick Mental Model + +Aztec is an L2 on Ethereum with **native privacy**. Three core ideas separate it from a typical EVM rollup: + +1. **Two kinds of state.** Public state works like Ethereum storage — everyone can read it. Private state is encrypted; only the owner can read it. +2. **Noir** is the smart contract language. It looks like Rust and compiles to zero-knowledge circuits. +3. **PXE** (Private eXecution Environment) is a local client that manages your private state and builds proofs before sending transactions. There is no Ethereum equivalent. + +#### Ethereum-to-Aztec comparison table + +| Ethereum | Aztec | +| -------------------------- | --------------------------------------- | +| Solidity | Noir | +| `mapping(address => uint)` | `Map>` | +| No equivalent | Notes (private encrypted state) | +| `msg.sender` | `context.msg_sender()` | +| `public` function | `#[external("public")]` | +| No native equivalent | `#[external("private")]` | +| Manual commit-reveal | Built into the protocol | +| EOA accounts | Account abstraction | +| ETH for gas | Fee Payment Contracts, sponsored fees | + +**Key concepts in more detail:** + +- **Notes** — Private state primitives. Think of them as encrypted UTXOs that only the owner can decrypt and read. When you "write" private state, you create a note. When you "read" it, your PXE decrypts it locally. ([Aztec docs: Notes](https://docs.aztec.network/developers/docs/foundational-topics/state_management#notes)) +- **Public vs Private functions** — Public functions execute on the network (like Solidity). Private functions execute locally in your PXE and produce a proof that gets verified on-chain. ([Aztec docs: Functions](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/functions)) +- **PXE** — Your local execution environment. It stores your private notes, builds proofs, and submits transactions. Each user runs their own PXE. ([Aztec docs: PXE](https://docs.aztec.network/aztec/concepts/pxe)) +- **Account abstraction** — Every Aztec account is a smart contract. There are no EOAs. This repo uses Schnorr signature accounts. ([Aztec docs: Accounts](https://docs.aztec.network/aztec/concepts/accounts)) + +### 1.2 — The Pod Racing Game: What It Does + +The Pod Racing contract ([`src/main.nr`](./src/main.nr)) is a two-player competitive game where players allocate points across 5 tracks over 3 rounds. It naturally requires commit-reveal (players shouldn't see each other's moves), making it a perfect Aztec demo. + +**Game flow:** + +1. Player 1 **creates** a game with a unique ID +2. Player 2 **joins** the game +3. Both players **play 3 rounds privately** — each round they distribute up to 9 points across 5 tracks +4. Both players **finish/reveal** — their private round notes are summed and the totals published +5. Anyone **finalizes** — the winner is determined by who won more tracks (best of 5) + +**Rules:** + +- 2 players per game +- 5 tracks, 3 rounds +- Each round: distribute up to 9 points across the 5 tracks +- After all rounds, each track's total is compared between players +- The player who wins 3+ tracks wins the game + +> Reference: top comment block in `src/main.nr` (lines 1-15) + +### 1.3 — Contract Walkthrough: Storage + +Open `src/main.nr` and look at the `Storage` struct (lines 39-56): + +```rust +#[storage] +struct Storage { + admin: PublicMutable, + races: Map, Context>, + progress: Map, Context>, Context>, + win_history: Map, Context>, +} +``` + +What each field does: + +| Field | Type | Visibility | Purpose | +| ------------- | ---------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- | +| `admin` | `PublicMutable` | Public | Contract administrator address, set in constructor | +| `races` | `Map>` | Public | Maps `game_id` to a `Race` struct with player addresses, round progress, and final scores | +| `progress` | `Map>>` | **Private** | Maps `game_id` → `player` → set of private notes containing that player's round choices | +| `win_history` | `Map>` | Public | Career win count per player (leaderboard) | + +**Conceptual Solidity equivalent:** + +```solidity +// Solidity (approximate) +contract PodRacing { + address public admin; + mapping(uint256 => Race) public races; + // No Solidity equivalent for private state! + // Aztec's `progress` stores encrypted data only the owner can read + mapping(address => uint256) public winHistory; +} +``` + +**State variable types — one sentence each:** + +- [`PublicMutable`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#publicmutable) — A single public value that can be read and written by public functions. Like a Solidity state variable. +- [`Map`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#map) — A key-value mapping, like Solidity's `mapping`. +- [`PrivateSet`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#privateset) — A set of private notes. Notes can be inserted, read (by owner), and nullified. +- [`Owned`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#owned) — A wrapper that scopes private state to a specific owner, so `progress.at(game_id).at(player)` returns only that player's notes. + +### 1.4 — Contract Walkthrough: Public Functions + +These functions should feel familiar if you've written Solidity. + +#### `constructor()` (line 58-62) + +```rust +#[external("public")] +#[initializer] +fn constructor(admin: AztecAddress) { + self.storage.admin.write(admin); +} +``` + +Sets the admin address. The `#[initializer]` macro means this runs once at deployment, like a Solidity constructor. + +#### `create_game()` (line 67-79) + +```rust +#[external("public")] +fn create_game(game_id: Field) { + assert(self.storage.races.at(game_id).read().player1.eq(AztecAddress::zero())); + let game = Race::new( + self.context.msg_sender().unwrap(), + TOTAL_ROUNDS, + self.context.block_number() + GAME_LENGTH, + ); + self.storage.races.at(game_id).write(game); +} +``` + +Creates a new game. Checks the game ID isn't taken (player1 must be zero address), then writes a new `Race` struct with the caller as player1 and an expiration time. + +#### `join_game()` (line 83-90) + +```rust +#[external("public")] +fn join_game(game_id: Field) { + let maybe_existing_game = self.storage.races.at(game_id).read(); + let joined_game = maybe_existing_game.join(self.context.msg_sender().unwrap()); + self.storage.races.at(game_id).write(joined_game); +} +``` + +A second player joins. The `Race::join()` method validates that player1 exists, the player2 slot is empty, and the joiner isn't player1. + +#### `finalize_game()` (line 222-232) + +```rust +#[external("public")] +fn finalize_game(game_id: Field) { + let game_in_progress = self.storage.races.at(game_id).read(); + let winner = game_in_progress.calculate_winner(self.context.block_number()); + let previous_wins = self.storage.win_history.at(winner).read(); + self.storage.win_history.at(winner).write(previous_wins + 1); +} +``` + +After both players have revealed, this compares track scores, determines the winner, and updates the leaderboard. + +#### The `Race` struct ([`src/race.nr`](./src/race.nr)) + +The `Race` struct (lines 8-38) stores all public game state. It has 17 fields: + +```rust +pub struct Race { + pub player1: AztecAddress, // Player 1 address + pub player2: AztecAddress, // Player 2 address + pub total_rounds: u8, // Always 3 + pub player1_round: u8, // Player 1's current round (0-3) + pub player2_round: u8, // Player 2's current round (0-3) + pub player1_track1_final: u64, // Player 1's revealed total for track 1 + pub player1_track2_final: u64, // ... track 2 + pub player1_track3_final: u64, // ... track 3 + pub player1_track4_final: u64, // ... track 4 + pub player1_track5_final: u64, // ... track 5 + pub player2_track1_final: u64, // Player 2's revealed total for track 1 + pub player2_track2_final: u64, // ... track 2 + pub player2_track3_final: u64, // ... track 3 + pub player2_track4_final: u64, // ... track 4 + pub player2_track5_final: u64, // ... track 5 + pub end_block: u32, // Block when game expires +} +``` + +Key methods: + +- **`Race::new()`** — Creates a game with player1, zero'd scores, and an expiration block +- **`Race::join()`** — Adds player2 after validating the game is joinable and the player isn't playing themselves +- **`Race::calculate_winner()`** — Compares each track's totals, counts wins per player, returns the address of whoever won 3+ tracks (ties go to player2) + +### 1.5 — Contract Walkthrough: Private Functions (The Key Difference) + +This is the "aha moment" — the part with no Ethereum equivalent. + +#### `play_round()` (line 99-131) + +```rust +#[external("private")] +fn play_round( + game_id: Field, + round: u8, + track1: u8, track2: u8, track3: u8, track4: u8, track5: u8, +) { + // 1. Validate point budget + assert(track1 + track2 + track3 + track4 + track5 < 10); + + let player = self.context.msg_sender().unwrap(); + + // 2. Create a private note with the player's choices + self.storage.progress + .at(game_id) + .at(player) + .insert(GameRoundNote::new(track1, track2, track3, track4, track5, round, player)) + .deliver(MessageDelivery.CONSTRAINED_ONCHAIN); + + // 3. Enqueue a public call to increment the round counter + self.enqueue(PodRacing::at(self.context.this_address()).validate_and_play_round( + player, game_id, round, + )); +} +``` + +Three things happen here that have no direct Ethereum equivalent: + +1. **Point constraint** — `track1 + track2 + ... < 10` is enforced in the ZK circuit. The prover can't cheat. +2. **Creating a private note** — `.insert(GameRoundNote::new(...))` stores the round choices as an encrypted note. Only the player can read it later. The `.deliver(MessageDelivery.CONSTRAINED_ONCHAIN)` commits the note's hash on-chain without revealing the content. +3. **Enqueuing a public call** — `self.enqueue(...)` schedules a public function to run after the private proof is verified. This updates the round counter publicly (so both players can see progress) without revealing the point allocation. + +#### `finish_game()` (line 149-184) + +```rust +#[external("private")] +fn finish_game(game_id: Field) { + let player = self.context.msg_sender().unwrap(); + + // Read all private notes for this player in this game + let totals = self.storage.progress.at(game_id).at(player) + .get_notes(NoteGetterOptions::new()); + + // Sum up points per track across all rounds + let mut total_track1: u64 = 0; + // ... (same for tracks 2-5) + for i in 0..TOTAL_ROUNDS { + total_track1 += totals.get(i as u32).note.track1 as u64; + // ... (same for tracks 2-5) + } + + // Enqueue public call to store the revealed totals + self.enqueue(PodRacing::at(self.context.this_address()).validate_finish_game_and_reveal( + player, game_id, + total_track1, total_track2, total_track3, total_track4, total_track5, + )); +} +``` + +This is the "reveal" phase: + +- **Reading own private notes** — `get_notes(NoteGetterOptions::new())` retrieves the player's encrypted round notes. Only the owner's PXE can decrypt these. +- **Summing and publishing** — The totals are calculated privately, then the enqueued public call writes them on-chain for everyone to see. + +#### `GameRoundNote` (`src/game_round_note.nr`) + +```rust +#[note] +pub struct GameRoundNote { + pub track1: u8, + pub track2: u8, + pub track3: u8, + pub track4: u8, + pub track5: u8, + pub round: u8, + pub owner: AztecAddress, +} +``` + +The `#[note]` macro makes this a private state primitive. Each note stores one round's point allocation and the owner's address. Only the owner can read it. + +#### Internal functions: `#[only_self]` + +Two functions are marked `#[only_self]`, meaning they can only be called by the contract itself (via `self.enqueue(...)`): + +- **`validate_and_play_round`** (line 136-142) — Validates the round is sequential and increments the player's round counter +- **`validate_finish_game_and_reveal`** (line 189-211) — Stores the player's revealed track totals, checking they haven't already been revealed + +**Key insight:** On Ethereum, commit-reveal requires at least 2 transactions (one to commit, one to reveal after a delay). On Aztec, the "commit" happens automatically when a private function creates a note — the data is committed on-chain (as a hash) without ever being visible. The "reveal" is a separate transaction, but the privacy was enforced by the protocol the whole time. + +### 1.6 — Game Flow: What's Private, What's Public + +Here's exactly what an outside observer can and cannot see at each step: + +| Step | Function | Type | Observer **CAN** see | Observer **CANNOT** see | +| ---- | --------------- | ----------------- | --------------------------------------------------------- | ---------------------------------------------- | +| 1 | `create_game` | public | Game created, player1 address, expiration block | Nothing hidden | +| 2 | `join_game` | public | Player2 joined, both addresses | Nothing hidden | +| 3 | `play_round` | private -> public | Round counter incremented (e.g. "player1 played round 1") | Point allocation across tracks | +| 4 | `finish_game` | private -> public | Final track totals revealed (e.g. "player1: 7,7,7,3,3") | Individual round allocations | +| 5 | `finalize_game` | public | Winner declared, leaderboard updated | Nothing hidden (all data public at this point) | + +The critical privacy window is between steps 3 and 4: both players have committed their strategies (as private notes), but neither can see the other's choices. This prevents the second player from gaining an advantage by observing the first player's moves. + +--- + +## Phase 2: Compile and Test in the Cloud (Zero Install) + +**Goal:** Get hands-on with compilation and Noir tests using only a GitHub Codespace. + +### 2.1 — Launch a GitHub Codespace + +1. Go to the [aztec-starter repo on GitHub](https://github.com/AztecProtocol/aztec-starter) +2. Click the green **Code** button, then **Codespaces** tab, then **Create codespace on main** +3. Wait for the Codespace to build and the `postCreateCommand` to finish + +The `.devcontainer/` configures: + +- **Base image:** Ubuntu 24.04 with Node.js v22.15.0 +- **Docker-in-Docker** for running the Aztec local network +- **Aztec CLI** installed via `VERSION=4.0.0-nightly.20260204 bash -i <(curl -sL https://install.aztec.network/4.0.0-nightly.20260204)` +- **VS Code extension:** `noir-lang.vscode-noir` for Noir syntax highlighting +- **Dependencies:** `yarn install` runs automatically + +### 2.2 — Compile the Contract + +```bash +yarn compile +``` + +This runs `aztec compile`, which compiles the Noir contract in `src/main.nr` to artifacts in `./target/`. This is like running `forge build` in Foundry. + +Then generate TypeScript bindings: + +```bash +yarn codegen +``` + +This runs `aztec codegen target --outdir src/artifacts` and generates `./src/artifacts/PodRacing.ts` — a TypeScript wrapper class (like TypeChain for Solidity). The generated `PodRacingContract` class gives you: + +- `PodRacingContract.deploy(wallet, admin)` — deploy a new instance +- `PodRacingContract.at(address, wallet)` — connect to an existing instance +- `contract.methods.create_game(gameId)` — call any contract function + +> Reference: `Nargo.toml` is the project manifest (like `foundry.toml`). It specifies the package name, type (`contract`), and the `aztec-nr` dependency version. + +### 2.3 — Run Noir Unit Tests (No Network Required) + +```bash +yarn test:nr +``` + +This runs `aztec test`, which uses Aztec's **TXE** (Testing eXecution Environment) — a lightweight test runtime similar to Foundry's `forge test`. No network or Docker required. + +#### Test structure + +Tests live in `src/test/`: + +- `mod.nr` — declares the test modules +- `utils.nr` — test setup (deploy contract, create admin) +- `helpers.nr` — reusable helpers (strategies, game setup, round playing) +- `pod_racing.nr` — the actual test cases + +#### Key test patterns in `src/test/pod_racing.nr` + +**Basic initialization test:** + +```rust +#[test] +unconstrained fn test_initializer() { + let (mut env, contract_address, admin) = utils::setup(); + env.public_context_at(contract_address, |context| { + let current_admin = context.storage_read(PodRacing::storage_layout().admin.slot); + assert_eq(current_admin, admin); + }); +} +``` + +The `unconstrained` keyword means this test runs outside the ZK circuit (it's a test, not a provable function). `utils::setup()` deploys a fresh contract and returns the environment, contract address, and admin. + +**Expected failure test:** + +```rust +#[test(should_fail)] +unconstrained fn test_fail_play_round_too_many_points() { + // ... setup ... + // Try to allocate 10 points (2+2+2+2+2) — should fail because limit is 9 + env.call_private( + player1, + PodRacing::at(contract_address).play_round(game_id, 1, 2, 2, 2, 2, 2) + ); +} +``` + +The `#[test(should_fail)]` attribute is like Foundry's `vm.expectRevert()`. + +**Full game flow test (`test_full_game_flow`):** + +This test creates a game, has both players play all 3 rounds with specific strategies, calls `finish_game` for both, and verifies the exact stored scores. It uses helper functions from `helpers.nr`. + +#### Test helpers (`src/test/helpers.nr`) + +Reusable allocation strategies: + +```rust +pub unconstrained fn balanced_allocation() -> (u8, u8, u8, u8, u8) { (2, 2, 2, 2, 1) } +pub unconstrained fn aggressive_allocation() -> (u8, u8, u8, u8, u8) { (3, 3, 3, 0, 0) } +pub unconstrained fn defensive_allocation() -> (u8, u8, u8, u8, u8) { (0, 0, 1, 4, 4) } +``` + +And higher-level helpers: + +```rust +pub unconstrained fn setup_two_player_game(env, contract_address, player1, player2, game_id) { ... } +pub unconstrained fn play_all_rounds_with_strategy(env, contract_address, player, game_id, allocations) { ... } +``` + +#### Test setup (`src/test/utils.nr`) + +```rust +pub unconstrained fn setup() -> (TestEnvironment, AztecAddress, AztecAddress) { + let mut env = TestEnvironment::new(); + let admin = env.create_light_account(); + let initializer_call_interface = PodRacing::interface().constructor(admin); + let contract_address = + env.deploy("PodRacing").with_public_initializer(admin, initializer_call_interface); + (env, contract_address, admin) +} +``` + +**Ethereum analogies:** + +- `env.call_public(player, ...)` is like `vm.prank(player)` + calling a function in Foundry +- `env.call_private(player, ...)` is the same, but for private functions (no Foundry equivalent) +- `context.storage_read(slot)` is like `vm.load(address, slot)` in Foundry +- `env.create_light_account()` creates a test account + +--- + +## Phase 3: Local Development Setup + +**Goal:** Set up a full local Aztec development environment. + +### 3.1 — Install Prerequisites + +**Node.js v22.15.0** — use [nvm](https://github.com/nvm-sh/nvm) or your preferred version manager. + +**Aztec toolkit:** + +```bash +bash -i <(curl -s https://install.aztec.network) +``` + +Install the correct version: + +```bash +export VERSION=3.0.0-devnet.6-patch.1 +aztec-up && docker pull aztecprotocol/aztec:$VERSION && docker tag aztecprotocol/aztec:$VERSION aztecprotocol/aztec:latest +``` + +**Project dependencies:** + +```bash +yarn install +``` + +### 3.2 — Start the Local Network + +In a separate terminal: + +```bash +aztec start --local-network +``` + +This starts: + +- **Aztec node** — processes transactions, builds blocks +- **PXE** — Private eXecution Environment on `localhost:8080` +- **Anvil L1 chain** — local Ethereum L1 on `localhost:8545` +- **Protocol contracts** — deployed automatically on the L1 + +This is the Aztec equivalent of running `anvil` or `npx hardhat node`. + +> **Warning:** If you restart the local network, delete the `./store` directory to avoid stale PXE state: +> +> ```bash +> rm -rf ./store +> ``` + +### 3.3 — Understand the Config System + +The project uses JSON config files selected by the `AZTEC_ENV` environment variable. + +**`config/config.ts`** — A `ConfigManager` singleton that loads the appropriate JSON file: + +```typescript +const env = process.env.AZTEC_ENV || "local-network"; +this.configPath = path.resolve(process.cwd(), `config/${env}.json`); +``` + +**`config/local-network.json`:** + +```json +{ + "name": "local-network", + "environment": "local", + "network": { + "nodeUrl": "http://localhost:8080", + "l1RpcUrl": "http://localhost:8545", + "l1ChainId": 31337 + } +} +``` + +**`config/devnet.json`:** + +```json +{ + "name": "devnet", + "environment": "devnet", + "network": { + "nodeUrl": "https://next.devnet.aztec-labs.com", + "l1RpcUrl": "https://ethereum-sepolia-rpc.publicnode.com", + "l1ChainId": 11155111 + } +} +``` + +Key exports from `config/config.ts`: + +- `getAztecNodeUrl()` — returns the node URL for the current environment +- `getTimeouts()` — returns environment-specific timeout values (local: 60s tx, devnet: 180s tx) +- `getEnv()` — returns the environment name (`"local-network"` or `"devnet"`) + +--- + +## Phase 4: Deploy and Interact + +**Goal:** Deploy accounts and contracts, interact with the Pod Racing game. + +### 4.1 — Deploy an Account + +Every Aztec account is a smart contract. There are no Externally Owned Accounts (EOAs). You must deploy your account before you can send transactions. + +**How it works (`src/utils/deploy_account.ts`):** + +1. Generate keys: `Fr.random()` for the secret key and salt, `GrumpkinScalar.random()` for the signing key +2. Create a Schnorr account: `wallet.createSchnorrAccount(secretKey, salt, signingKey)` +3. Deploy it using sponsored fees: `account.getDeployMethod().send({ fee: { paymentMethod: sponsoredPaymentMethod } })` + +**Sponsored fees (`src/utils/sponsored_fpc.ts`):** + +The `SponsoredFPC` is a canonical Fee Payment Contract deployed at a deterministic address (salt = 0). It pays transaction fees on behalf of users, useful for onboarding when users don't have Fee Juice yet. On the local network it's pre-deployed. + +```typescript +const SPONSORED_FPC_SALT = new Fr(0); +export async function getSponsoredFPCInstance(): Promise { + return await getContractInstanceFromInstantiationParams( + SponsoredFPCContract.artifact, + { + salt: SPONSORED_FPC_SALT, + }, + ); +} +``` + +**Run it:** + +```bash +yarn deploy-account +``` + +Save the output (secret key, signing key, salt) to a `.env` file. See `.env.example` for the format: + +``` +SECRET="0x..." +SIGNING_KEY="0x..." +SALT="0x..." +AZTEC_ENV=local-network +``` + +### 4.2 — Deploy the Contract + +**How it works (`scripts/deploy_contract.ts`):** + +1. Set up wallet via `setupWallet()` (connects to the node, creates a TestWallet) +2. Register the `SponsoredFPC` for fee payment +3. Deploy a Schnorr account (or use one from env) +4. Deploy the contract: + +```typescript +const podRacingContract = await PodRacingContract.deploy(wallet, address) + .send({ + from: address, + fee: { paymentMethod: sponsoredPaymentMethod }, + }) + .deployed({ timeout: timeouts.deployTimeout }); +``` + +**Run it:** + +```bash +yarn deploy +``` + +The output includes the contract address, admin address, and instantiation data (salt, deployer, constructor args). Save these for interacting with the contract later. + +### 4.3 — Interact with a Deployed Contract + +**How it works (`scripts/interaction_existing_contract.ts`):** + +1. Load your account from env: `getAccountFromEnv(wallet)` reads `SECRET`, `SIGNING_KEY`, and `SALT` from `.env` +2. Reconstruct the contract instance from env vars (`CONTRACT_SALT`, `CONTRACT_DEPLOYER`, `CONTRACT_CONSTRUCTOR_ARGS`) +3. Register the contract with the wallet: `wallet.registerContract(instance, PodRacingContract.artifact)` +4. Call methods: + +```typescript +const podRacingContract = await PodRacingContract.at(contractAddress, wallet); +await podRacingContract.methods + .create_game(gameId) + .send({ + from: address, + fee: { paymentMethod: sponsoredPaymentMethod }, + }) + .wait({ timeout: timeouts.txTimeout }); +``` + +Set the env vars from your deploy output, then run: + +```bash +yarn interaction-existing-contract +``` + +### 4.4 — Run E2E Tests + +**How it works (`src/test/e2e/index.test.ts`):** + +The `beforeAll` block: + +1. Sets up a `TestWallet` via `setupWallet()` +2. Registers the `SponsoredFPC` +3. Creates two Schnorr player accounts with random keys +4. Deploys the Pod Racing contract with player1 as admin + +Key tests: + +- **Creates a game** — calls `create_game` and checks `TxStatus.SUCCESS` +- **Allows a second player to join** — sets up a game with both players +- **Plays a complete round** — private function call, verifies success +- **Rejects rounds with too many points** — expects the transaction to throw +- **Plays a full game from start to finish** — all rounds, both players, finish and reveal +- **Maintains privacy of round choices** — verifies round can be played without revealing allocations + +**Run them:** + +```bash +# Both Noir unit tests and TypeScript E2E tests +yarn test + +# Just TypeScript E2E tests (requires local network running) +yarn test:js + +# Just Noir unit tests (no network required) +yarn test:nr +``` + +--- + +## Phase 5: Write Your Own Code + +**Goal:** Modify the contract and write tests. + +### 5.1 — Guided Exercise: Add a Forfeit Function + +Add a `forfeit_game` function to `src/main.nr` that lets a player concede: + +```rust +#[external("public")] +fn forfeit_game(game_id: Field) { + let game = self.storage.races.at(game_id).read(); + let caller = self.context.msg_sender().unwrap(); + + // Only a player in this game can forfeit + assert(caller.eq(game.player1) | caller.eq(game.player2)); + + // The other player wins + let winner = if caller.eq(game.player1) { + game.player2 + } else { + game.player1 + }; + + // Update win history + let previous_wins = self.storage.win_history.at(winner).read(); + self.storage.win_history.at(winner).write(previous_wins + 1); +} +``` + +Compile and regenerate TypeScript bindings: + +```bash +yarn compile && yarn codegen +``` + +### 5.2 — Write a Noir Test + +Add a test to `src/test/pod_racing.nr`: + +```rust +#[test] +unconstrained fn test_forfeit_game() { + let (mut env, contract_address, _) = utils::setup(); + let player1 = env.create_light_account(); + let player2 = env.create_light_account(); + let game_id = helpers::TEST_GAME_ID_9; + + // Setup game + helpers::setup_two_player_game(&mut env, contract_address, player1, player2, game_id); + + // Player 1 forfeits + env.call_public(player1, PodRacing::at(contract_address).forfeit_game(game_id)); + + // Verify player2's win count increased + env.public_context_at(contract_address, |context| { + let win_slot = derive_storage_slot_in_map( + PodRacing::storage_layout().win_history.slot, + player2.to_field() + ); + let wins = context.storage_read(win_slot); + assert_eq(wins, 1); + }); +} +``` + +Run: + +```bash +yarn test:nr +``` + +### 5.3 — Write a TypeScript E2E Test + +Add a test case to `src/test/e2e/index.test.ts`: + +```typescript +it("Allows a player to forfeit", async () => { + const gameId = new Fr(300); + + await setupGame( + contract, + gameId, + player1Account.address, + player2Account.address, + sponsoredPaymentMethod, + getTimeouts().txTimeout, + ); + + const tx = await contract.methods + .forfeit_game(gameId) + .send({ + from: player1Account.address, + fee: { paymentMethod: sponsoredPaymentMethod }, + }) + .wait({ timeout: getTimeouts().txTimeout }); + + expect(tx.status).toBe(TxStatus.SUCCESS); +}, 600000); +``` + +Run: + +```bash +yarn test:js +``` + +### 5.4 — Ideas for Further Modifications + +- **Easy:** Change `TOTAL_ROUNDS` from 3 to 5, and the point budget from 9 to 15. Update constants and re-run tests. +- **Medium:** Add a `get_game_state` unconstrained view function that returns the public `Race` data for a given game ID. +- **Hard:** Add token wagers — import `TokenContract`, have players deposit tokens when joining, and transfer the pot to the winner on finalization. + +--- + +## Phase 6: Advanced Topics + +**Goal:** Explore multi-wallet patterns, fee strategies, devnet, and profiling. + +### 6.1 — Multiple Wallets / Multiple PXEs + +**Why this matters:** In a real application, each player runs their own PXE. Player 1's PXE holds Player 1's private notes. Player 2's PXE holds Player 2's. + +**How it works (`scripts/multiple_wallet.ts`):** + +The script creates two independent PXE instances (wallet1, wallet2), each with their own key store: + +```typescript +const store1 = await createStore('pxe1', { dataDirectory: 'store', ... }); +const store2 = await createStore('pxe2', { dataDirectory: 'store', ... }); +const wallet1 = await TestWallet.create(node, fullConfig, { store: store1 }); +const wallet2 = await TestWallet.create(node, fullConfig, { store: store2 }); +``` + +It then deploys a Token contract from wallet1, creates an account on wallet2, mints tokens to wallet2's account, registers the token contract on wallet2, and reads balances. + +Key concept: `wallet2.registerContract(...)` is necessary because wallet2's PXE doesn't automatically know about contracts deployed by wallet1. Each PXE maintains its own registry. + +```bash +yarn multiple-wallet +``` + +### 6.2 — Fee Payment Strategies + +**`scripts/fees.ts`** demonstrates all four fee payment methods: + +1. **Sponsored** (`SponsoredFeePaymentMethod`) — A pre-deployed SponsoredFPC contract pays your fees. Simplest option, used throughout this repo. + +2. **Fee Juice** (`FeeJuicePaymentMethodWithClaim`) — Bridge ETH from L1 to get Fee Juice (native L2 gas token), then use it to pay fees directly. Requires L1 interaction. + +3. **Private fees** (`PrivateFeePaymentMethod`) — Pay fees through a Fee Payment Contract (FPC) using a private token transfer. The fee payment itself is private. + +4. **Public fees** (`PublicFeePaymentMethod`) — Same FPC mechanism but the token transfer is public. + +```bash +yarn fees +``` + +### 6.3 — Deploying to Devnet + +All scripts support a `::devnet` suffix: + +```bash +yarn deploy::devnet +yarn deploy-account::devnet +yarn interaction-existing-contract::devnet +yarn test::devnet +``` + +Devnet uses real provers and connects to the Aztec devnet at `https://next.devnet.aztec-labs.com` with Sepolia as the L1. Timeouts are longer (deploy: 20 min, tx: 3 min) to account for real proving time. + +### 6.4 — Transaction Profiling + +**`scripts/profile_deploy.ts`** shows how to profile a transaction: + +```typescript +const profileTx = await PodRacingContract.deploy(wallet, address).profile({ + profileMode: "full", + from: address, +}); +console.dir(profileTx, { depth: 2 }); +``` + +The `.profile()` method runs the transaction through the prover and returns detailed metrics about gate counts and proving time. + +```bash +yarn profile +``` + +### 6.5 — Querying Blocks + +**`scripts/get_block.ts`** shows how to query the Aztec node directly: + +```typescript +const node = createAztecNodeClient(nodeUrl); +let block = await node.getBlock(BlockNumber(1)); +console.log(block); +console.log(await block?.hash()); +``` + +```bash +yarn get-block +``` + +--- + +## Appendix + +### A. File Map + +| File | Purpose | +| ------------------------------------------ | ------------------------------------------------------ | +| `src/main.nr` | Pod Racing contract — all public and private functions | +| `src/race.nr` | `Race` struct — public game state and logic | +| `src/game_round_note.nr` | `GameRoundNote` — private note for round choices | +| `src/test/mod.nr` | Declares test modules | +| `src/test/pod_racing.nr` | Noir unit tests for the contract | +| `src/test/helpers.nr` | Test helpers — strategies, game setup | +| `src/test/utils.nr` | Test setup — deploy contract, create admin | +| `src/test/e2e/index.test.ts` | TypeScript E2E tests (Jest) | +| `src/artifacts/PodRacing.ts` | Generated TypeScript contract bindings | +| `src/utils/deploy_account.ts` | Deploy a Schnorr account | +| `src/utils/sponsored_fpc.ts` | SponsoredFPC instance helper | +| `src/utils/setup_wallet.ts` | Wallet setup (connects to node) | +| `src/utils/create_account_from_env.ts` | Load account from `.env` vars | +| `scripts/deploy_contract.ts` | Deploy the Pod Racing contract | +| `scripts/deploy_account.ts` | Deploy account entry point | +| `scripts/interaction_existing_contract.ts` | Interact with a deployed contract | +| `scripts/multiple_wallet.ts` | Multi-PXE / multi-wallet demo | +| `scripts/fees.ts` | Fee payment strategies demo | +| `scripts/profile_deploy.ts` | Transaction profiling | +| `scripts/get_block.ts` | Block querying | +| `config/config.ts` | Config manager (loads JSON by env) | +| `config/local-network.json` | Local network configuration | +| `config/devnet.json` | Devnet configuration | +| `Nargo.toml` | Noir project manifest | +| `.devcontainer/devcontainer.json` | GitHub Codespace configuration | +| `.devcontainer/Dockerfile` | Codespace Docker image | +| `.env.example` | Example environment variables | + +### B. Common Commands + +| Command | Description | +| ------------------------------------ | -------------------------------------------------- | +| `yarn compile` | Compile Noir contract to `./target/` | +| `yarn codegen` | Generate TypeScript bindings to `./src/artifacts/` | +| `yarn test` | Run both Noir and TypeScript tests | +| `yarn test:nr` | Run Noir unit tests only (no network) | +| `yarn test:js` | Run TypeScript E2E tests (needs local network) | +| `yarn deploy` | Deploy account + contract to local network | +| `yarn deploy-account` | Deploy a Schnorr account | +| `yarn interaction-existing-contract` | Interact with a deployed contract | +| `yarn multiple-wallet` | Multi-PXE demo | +| `yarn fees` | Fee payment methods demo | +| `yarn profile` | Profile a transaction | +| `yarn get-block` | Query block data | +| `yarn clean` | Delete `./src/artifacts` and `./target` | +| `yarn clear-store` | Delete `./store` (PXE data) | +| `yarn deploy::devnet` | Deploy to devnet | +| `yarn test::devnet` | Run E2E tests on devnet | + +### C. Troubleshooting + +| Problem | Solution | +| -------------------------------------------------------- | ------------------------------------------------------------------------ | +| "Store" or PXE errors after restarting the local network | Delete `./store`: `rm -rf ./store` | +| Compilation errors after updating dependencies | Run `yarn compile` again | +| Timeout errors on devnet | Check timeout values in `config/devnet.json` (deploy: 20 min, tx: 3 min) | +| "Contract not registered" error | Call `wallet.registerContract(instance, artifact)` before interacting | +| Account not found | Ensure `.env` has correct `SECRET`, `SIGNING_KEY`, and `SALT` values | +| Local network not starting | Ensure Docker is running and the correct Aztec version is installed | + +### D. Links + +- [Aztec Documentation](https://docs.aztec.network) +- [Aztec-nr (Noir framework for Aztec)](https://github.com/AztecProtocol/aztec-nr) +- [Noir Language](https://noir-lang.org) +- [Aztec Discord](https://discord.gg/aztec) +- [This repository](https://github.com/AztecProtocol/aztec-starter) diff --git a/README.md b/README.md index 98e750a..6959a3b 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ This repo is meant to be a starting point for learning to write Aztec contracts and tests on the Aztec local network (local development environment). It includes an example contract, useful commands in `package.json` and helpful scripts in `./scripts`. +**New to Aztec? Start with the [Onboarding Guide](./ONBOARDING.md)** — a progressive walkthrough that takes Ethereum developers from reading code to deploying on devnet. + You can find the **Pod Racing Game contract** in `./src/main.nr`. A simple integration test is in `./src/test/e2e/index.test.ts`. The Pod Racing contract is a two-player competitive game where players allocate points across 5 tracks over multiple rounds. The game demonstrates Aztec's private state capabilities - round choices remain private until players reveal their final scores. From 4b00caf67646a2d3a8a4380543cc2d82be42a29d Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Mon, 9 Feb 2026 17:25:21 -0500 Subject: [PATCH 2/9] Clarify that Aztec's key benefit is private execution composability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the "Key insight" section in ONBOARDING.md to explain that the real advantage isn't just easier commit-reveal, but composability in private execution — private functions validating constraints over hidden inputs, storing encrypted state, and interacting with public state atomically. Co-Authored-By: Claude Opus 4.6 --- ONBOARDING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ONBOARDING.md b/ONBOARDING.md index a70a768..58d055c 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -304,6 +304,8 @@ Two functions are marked `#[only_self]`, meaning they can only be called by the **Key insight:** On Ethereum, commit-reveal requires at least 2 transactions (one to commit, one to reveal after a delay). On Aztec, the "commit" happens automatically when a private function creates a note — the data is committed on-chain (as a hash) without ever being visible. The "reveal" is a separate transaction, but the privacy was enforced by the protocol the whole time. +But the deeper point isn't just "commit-reveal is easier." The real benefit of Aztec is **composability in private execution**. In `play_round`, a private function validates a constraint (`track1 + ... < 10`), stores an encrypted note, and enqueues a public state update — all in one atomic transaction. On a public blockchain, you simply cannot compose contract logic over hidden inputs like this. Any "private" scheme on Ethereum (e.g. commit-reveal, ZK proofs submitted to a verifier contract) requires the application developer to build all the privacy infrastructure themselves, and each private component is an isolated island. On Aztec, private functions call other private functions, read private state, and interact with public state through a unified execution model — privacy is a first-class property of the entire contract system, not a bolt-on per application. + ### 1.6 — Game Flow: What's Private, What's Public Here's exactly what an outside observer can and cannot see at each step: From b8b4bb7076e06596af98ef676e37556c4aec9778 Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Tue, 10 Feb 2026 16:26:10 -0500 Subject: [PATCH 3/9] Add netcat-openbsd to Dockerfile and update ONBOARDING.md for test patterns link --- .devcontainer/Dockerfile | 3 ++- .devcontainer/devcontainer.json | 12 ++++++------ ONBOARDING.md | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 72bc443..3ee88fe 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -20,7 +20,8 @@ RUN apt update && apt install -y \ jq \ python3 \ build-essential \ - ca-certificates + ca-certificates \ + netcat-openbsd # Install nvm, node and npm RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a602278..7188f8d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,14 +3,14 @@ "context": ".", "dockerfile": "Dockerfile" }, - "postCreateCommand": "yarn ", + "postCreateCommand": "yarn ", "customizations": { // Configure properties specific to VS Code. - "vscode": { - // Set *default* container specific settings.json values on container create. - "settings": {}, - "extensions": ["noir-lang.vscode-noir"] - } + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": {}, + "extensions": ["noir-lang.vscode-noir"] + } }, "workspaceMount": "source=${localWorkspaceFolder},target=/root/workspace,type=bind", "workspaceFolder": "/root/workspace" diff --git a/ONBOARDING.md b/ONBOARDING.md index 58d055c..b3b4708 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -379,7 +379,7 @@ Tests live in `src/test/`: - `helpers.nr` — reusable helpers (strategies, game setup, round playing) - `pod_racing.nr` — the actual test cases -#### Key test patterns in `src/test/pod_racing.nr` +#### Key test patterns in [`src/test/pod_racing.nr`](./src/test/pod_racing.nr) **Basic initialization test:** From 70a367e5c47957e51b5ee057f1a0aab8f108e327 Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Tue, 10 Feb 2026 16:49:07 -0500 Subject: [PATCH 4/9] Add file association for Noir syntax highlighting fallback in devcontainer.json --- .devcontainer/devcontainer.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7188f8d..8468608 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,12 @@ // Configure properties specific to VS Code. "vscode": { // Set *default* container specific settings.json values on container create. - "settings": {}, + "settings": { +// Noir syntax highlighting may not work in Codespaces; use Rust as fallback + "files.associations": { + "*.nr": "rust" + } + }, "extensions": ["noir-lang.vscode-noir"] } }, From 3d9cebecdf687fe9f3479eea0ac0f5c62a0527b1 Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Wed, 11 Feb 2026 12:29:40 -0500 Subject: [PATCH 5/9] run through and edits --- CLAUDE.md | 20 +++++++++++++++++++- ONBOARDING.md | 37 +++++++++++++++++-------------------- README.md | 10 +++------- config/local-network.json | 2 +- package.json | 2 +- scripts/get_block.ts | 9 ++++----- 6 files changed, 45 insertions(+), 35 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6848091..28c7f8e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,11 +6,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co 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. -**Aztec version pinned:** `3.0.0-devnet.6-patch.1` (check `Nargo.toml` and `package.json` for source of truth). +**Aztec version pinned:** `4.0.0-nightly.20260204` (check `Nargo.toml` and `package.json` for source of truth). ## Commands ### Build + ```bash yarn compile # Compile Noir contract (runs `aztec compile`) -> ./target/ yarn codegen # Generate TypeScript bindings -> ./src/artifacts/PodRacing.ts @@ -18,6 +19,7 @@ yarn compile && yarn codegen # Full rebuild (always run both after contract cha ``` ### Test + ```bash yarn test # Run ALL tests (Noir unit + TypeScript E2E) yarn test:nr # Noir unit tests only (no network needed, uses TXE) @@ -27,12 +29,14 @@ yarn test:js # TypeScript E2E tests only (requires local network runnin 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`). ### Local Network (required for `test:js`, scripts, and deploy) + ```bash aztec start --local-network # Start Aztec node + PXE + Anvil L1 rm -rf ./store # MUST delete after restarting local network ``` ### Scripts (all use `node --loader ts-node/esm`) + ```bash yarn deploy # Deploy account + Pod Racing contract yarn deploy-account # Deploy a Schnorr account only @@ -44,13 +48,16 @@ yarn get-block # Query block data ``` ### Devnet + Append `::devnet` to any script to target devnet (sets `AZTEC_ENV=devnet`): + ```bash yarn deploy::devnet yarn test::devnet ``` ### Clean + ```bash yarn clean # Delete ./src/artifacts and ./target yarn clear-store # Delete ./store (PXE data) @@ -59,22 +66,27 @@ yarn clear-store # Delete ./store (PXE data) ## Architecture ### Two Languages, One Contract + - **Noir** (`.nr` files in `src/`) — the smart contract, compiled to ZK circuits - **TypeScript** (`.ts` files in `scripts/`, `src/test/e2e/`, `src/utils/`) — deployment scripts, E2E tests, and utilities ### Contract Structure (`src/`) + - `main.nr` — Pod Racing contract entry point. Contains all public/private functions. Imports `mod test`, `mod game_round_note`, `mod race`. - `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()`. - `game_round_note.nr` — `GameRoundNote` private note storing one round's point allocation. The `#[note]` macro makes it a private state primitive. ### Key Aztec Pattern: Private-to-Public Flow + The contract demonstrates the core Aztec pattern where private functions enqueue public calls: + 1. `play_round` (private) — creates encrypted `GameRoundNote`, then enqueues `validate_and_play_round` (public) to update round counter 2. `finish_game` (private) — reads player's private notes, sums totals, then enqueues `validate_finish_game_and_reveal` (public) to publish scores Functions marked `#[only_self]` can only be called by the contract itself via `self.enqueue(...)`. ### TypeScript Side + - `config/config.ts` — Singleton `ConfigManager` that loads `config/local-network.json` or `config/devnet.json` based on `AZTEC_ENV`. Exports: `getAztecNodeUrl()`, `getTimeouts()`, `getEnv()`. - `src/utils/setup_wallet.ts` — Creates `TestWallet` connected to the Aztec node. Enables prover on non-local environments. - `src/utils/sponsored_fpc.ts` — Gets the canonical `SponsoredFPC` instance (salt=0) for sponsored fee payment. @@ -83,17 +95,22 @@ Functions marked `#[only_self]` can only be called by the contract itself via `s - `src/artifacts/PodRacing.ts` — **Generated file, do not edit.** TypeScript bindings for the contract. ### Test Structure + **Noir tests** (`src/test/`): Use TXE (Testing eXecution Environment), no network needed. + - `utils.nr` — `setup()` deploys contract, returns `(TestEnvironment, contract_address, admin)` - `helpers.nr` — Reusable allocation strategies and game setup helpers - `pod_racing.nr` — Test cases. Uses `#[test]` and `#[test(should_fail)]` attributes. **TypeScript E2E tests** (`src/test/e2e/`): Use Jest, require local network. + - `index.test.ts` — Pod Racing game lifecycle tests - `accounts.test.ts` — Account deployment and fee payment tests ### Environment Variables (`.env`) + See `.env.example` for format. Key vars: + - `SECRET`, `SIGNING_KEY`, `SALT` — Account credentials - `AZTEC_ENV` — `local-network` (default) or `devnet` - `POD_RACING_CONTRACT_ADDRESS`, `CONTRACT_SALT`, `CONTRACT_DEPLOYER`, `CONTRACT_CONSTRUCTOR_ARGS` — For interacting with existing contracts @@ -112,6 +129,7 @@ See `.env.example` for format. Key vars: ## ONBOARDING.md Maintenance This repo includes an `ONBOARDING.md` that serves as a progressive tutorial for Ethereum developers learning Aztec. **When making code changes, check if `ONBOARDING.md` references the changed code** (line numbers, function signatures, code snippets, struct definitions) and update it accordingly. Key sections that embed code: + - Phase 1 (1.3-1.6): Contract storage, public/private functions, game flow table - Phase 2 (2.3): Noir test patterns and helpers - Phase 5 (5.1-5.3): Guided exercises with code examples diff --git a/ONBOARDING.md b/ONBOARDING.md index b3b4708..922760f 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -9,7 +9,7 @@ This guide takes you from "reading code in a browser" to "deploying on devnet" - **Phases 1-2** need only a browser (read code, compile in a Codespace) - **Phases 3-6** need local tools (deploy, interact, extend, advanced topics) -**Aztec version pinned in this repo:** `3.0.0-devnet.6-patch.1` +**Aztec version pinned in this repo:** `4.0.0-nightly.20260204` (check `Nargo.toml` and `package.json` for source of truth) **Links:** @@ -466,14 +466,8 @@ pub unconstrained fn setup() -> (TestEnvironment, AztecAddress, AztecAddress) { **Aztec toolkit:** ```bash -bash -i <(curl -s https://install.aztec.network) -``` - -Install the correct version: - -```bash -export VERSION=3.0.0-devnet.6-patch.1 -aztec-up && docker pull aztecprotocol/aztec:$VERSION && docker tag aztecprotocol/aztec:$VERSION aztecprotocol/aztec:latest +export VERSION=4.0.0-nightly.20260204 +bash -i <(curl -sL https://install.aztec.network/$VERSION) ``` **Project dependencies:** @@ -560,7 +554,7 @@ Key exports from `config/config.ts`: Every Aztec account is a smart contract. There are no Externally Owned Accounts (EOAs). You must deploy your account before you can send transactions. -**How it works (`src/utils/deploy_account.ts`):** +**How it works ([`src/utils/deploy_account.ts`](src/utils/deploy_account.ts)):** 1. Generate keys: `Fr.random()` for the secret key and salt, `GrumpkinScalar.random()` for the signing key 2. Create a Schnorr account: `wallet.createSchnorrAccount(secretKey, salt, signingKey)` @@ -696,7 +690,7 @@ Add a `forfeit_game` function to `src/main.nr` that lets a player concede: #[external("public")] fn forfeit_game(game_id: Field) { let game = self.storage.races.at(game_id).read(); - let caller = self.context.msg_sender().unwrap(); + let caller = self.context.maybe_msg_sender().unwrap(); // Only a player in this game can forfeit assert(caller.eq(game.player1) | caller.eq(game.player2)); @@ -773,15 +767,18 @@ it("Allows a player to forfeit", async () => { getTimeouts().txTimeout, ); - const tx = await contract.methods - .forfeit_game(gameId) - .send({ - from: player1Account.address, - fee: { paymentMethod: sponsoredPaymentMethod }, - }) - .wait({ timeout: getTimeouts().txTimeout }); - - expect(tx.status).toBe(TxStatus.SUCCESS); + const tx = await contract.methods.forfeit_game(gameId).send({ + from: player1Account.address, + fee: { paymentMethod: sponsoredPaymentMethod }, + wait: { timeout: getTimeouts().txTimeout }, + }); + + expect([ + TxStatus.PROPOSED, + TxStatus.CHECKPOINTED, + TxStatus.PROVEN, + TxStatus.FINALIZED, + ]).toContain(tx.status); }, 600000); ``` diff --git a/README.md b/README.md index b5de402..929657d 100644 --- a/README.md +++ b/README.md @@ -43,15 +43,11 @@ Use **Node.js version 22.15.0**. Get the **local network, aztec-cli, and other tooling** with this command: ```bash -bash -i <(curl -s https://install.aztec.network) +export VERSION=4.0.0-nightly.20260204 +bash -i <(curl -sL https://install.aztec.network/$VERSION) ``` -Install the correct version of the toolkit with: - -```bash -export VERSION=3.0.0-devnet.6-patch.1 -aztec-up && docker pull aztecprotocol/aztec:$VERSION && docker tag aztecprotocol/aztec:$VERSION aztecprotocol/aztec:latest -``` +Then install project dependencies: ### Environment Configuration diff --git a/config/local-network.json b/config/local-network.json index 1734a0c..3ddcfbb 100644 --- a/config/local-network.json +++ b/config/local-network.json @@ -8,7 +8,7 @@ }, "settings": { "skipLocalNetwork": false, - "version": "3.0.0-devnet.6-patch.1" + "version": "4.0.0-nightly.20260204" }, "timeouts": { "deployTimeout": 120000, diff --git a/package.json b/package.json index 984bc61..264a7df 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "fees": "NODE_NO_WARNINGS=1 node --loader ts-node/esm scripts/fees.ts", "fees::devnet": "NODE_NO_WARNINGS=1 AZTEC_ENV=devnet node --loader ts-node/esm scripts/fees.ts", - "clean": "rm -rf ./src/artifacts ./target", + "clean": "rm -rf ./src/artifacts ./target ./codegenCache.json", "clear-store": "rm -rf ./store", "codegen": "aztec codegen target --outdir src/artifacts", "compile": "aztec compile", diff --git a/scripts/get_block.ts b/scripts/get_block.ts index 82b8878..a38f43d 100644 --- a/scripts/get_block.ts +++ b/scripts/get_block.ts @@ -4,11 +4,10 @@ import { getAztecNodeUrl } from "../config/config.js"; async function main() { - const nodeUrl = getAztecNodeUrl(); - const node = createAztecNodeClient(nodeUrl); - let block = await node.getBlock(BlockNumber(1)); - console.log(block) - console.log(await block?.hash()) + const nodeUrl = getAztecNodeUrl(); + const node = createAztecNodeClient(nodeUrl); + let block = await node.getBlock(BlockNumber(1)); + console.log(block?.header) } main() From 51d5fe39875f8989bb8878bc728880d26a2261db Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Wed, 11 Feb 2026 12:33:09 -0500 Subject: [PATCH 6/9] warning about codespace speed --- ONBOARDING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ONBOARDING.md b/ONBOARDING.md index 922760f..5ce3e23 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -324,6 +324,8 @@ The critical privacy window is between steps 3 and 4: both players have committe ## Phase 2: Compile and Test in the Cloud (Zero Install) +:warning: **Important:** This phase uses GitHub Codespaces to provide a cloud-based dev environment with all dependencies pre-installed. This can be slow to run on a basic codespace instance, you may want to jump directly to running this on your local machine in [Phase 3](#phase-3-local-development-setup). + **Goal:** Get hands-on with compilation and Noir tests using only a GitHub Codespace. ### 2.1 — Launch a GitHub Codespace From 6f38e8cd71905c7c03d8f723cdea6f1b3f4854c8 Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Mon, 23 Feb 2026 16:08:17 -0500 Subject: [PATCH 7/9] Clarify "private, then public" in game flow table and improve ONBOARDING.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explain that "private → public" means a private function enqueuing a public function within the same transaction, not a third function type. Also adds inline code comments, a Context explainer, a composability diagram, and various other improvements to the onboarding tutorial. Co-Authored-By: Claude Opus 4.6 --- ONBOARDING.md | 134 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 32 deletions(-) diff --git a/ONBOARDING.md b/ONBOARDING.md index 0a51255..844b3d1 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -33,17 +33,17 @@ Aztec is an L2 on Ethereum with **native privacy**. Three core ideas separate it #### Ethereum-to-Aztec comparison table -| Ethereum | Aztec | -| -------------------------- | --------------------------------------- | -| Solidity | Noir | -| `mapping(address => uint)` | `Map>` | -| No equivalent | Notes (private encrypted state) | -| `msg.sender` | `context.msg_sender()` | -| `public` function | `#[external("public")]` | -| No native equivalent | `#[external("private")]` | -| Manual commit-reveal | Built into the protocol | -| EOA accounts | Account abstraction | -| ETH for gas | Fee Payment Contracts, sponsored fees | +| Concept | Ethereum | Aztec | +| -------------------------------- | -------------------------- | --------------------------------------- | +| **Language** | Solidity | Noir | +| **Public state mapping** | `mapping(address => uint)` | `Map>` | +| **Private state model** | No equivalent | Notes, private encrypted state | +| **Caller context** | `msg.sender` | `context.msg_sender()` | +| **Public function declaration** | `public function` | `#[external("public")]` | +| **Private function declaration** | No native equivalent | `#[external("private")]` | +| **Commit-reveal pattern** | Manual commit-reveal | Built into the protocol | +| **Account model** | EOA accounts | Account abstraction | +| **Gas payment model** | ETH for gas | Fee Payment Contracts, sponsored fees | **Key concepts in more detail:** @@ -54,6 +54,8 @@ Aztec is an L2 on Ethereum with **native privacy**. Three core ideas separate it ### 1.2 — The Pod Racing Game: What It Does +Think of managing a pod racing team: you have limited resources (crew, fuel, parts) and 5 race tracks to compete on over 3 rounds. Each round you secretly decide how to spread your resources across the tracks — go all-in on a few, or spread thin across all five? After all rounds, your totals are revealed and whoever dominated more tracks wins the series. The game isn't about speed — it's about strategy under hidden information, which is exactly what Aztec's private state enables. + The Pod Racing contract ([`src/main.nr`](./src/main.nr)) is a two-player competitive game where players allocate points across 5 tracks over 3 rounds. It naturally requires commit-reveal (players shouldn't see each other's moves), making it a perfect Aztec demo. **Game flow:** @@ -81,13 +83,26 @@ Open `src/main.nr` and look at the `Storage` struct (lines 39-56): ```rust #[storage] struct Storage { + // Contract administrator address admin: PublicMutable, + + // Maps game_id -> Race struct containing public game state + // Stores player addresses, round progress, and final track scores races: Map, Context>, + + // Maps game_id -> player_address -> private notes containing that player's round choices + // Each GameRoundNote stores the point allocation for one round + // This data remains private until the player calls finish_game progress: Map, Context>, Context>, + + // Maps player address -> total number of wins + // Public leaderboard tracking career victories win_history: Map, Context>, } ``` +**What is `Context`?** You'll notice `Context` appears as a generic parameter throughout the storage definition. In Aztec, the context is the execution environment passed to every function — it's how your contract accesses blockchain state like `context.msg_sender()` (the caller's address) and `context.block_number()`. Think of it as an expanded version of Solidity's global variables (`msg.sender`, `block.number`, etc.), but packaged as an object. The `` generic on storage types lets the same storage struct work in both public and private execution contexts. You don't need to construct it yourself — the framework provides `self.context` automatically in every contract function. + What each field does: | Field | Type | Visibility | Purpose | @@ -138,9 +153,14 @@ Sets the admin address. The `#[initializer]` macro means this runs once at deplo ```rust #[external("public")] fn create_game(game_id: Field) { + // Ensure this game_id hasn't been used yet (player1 must be zero address) assert(self.storage.races.at(game_id).read().player1.eq(AztecAddress::zero())); + + let player1 = self.context.maybe_msg_sender().unwrap(); + + // Initialize a new Race with the caller as player1 let game = Race::new( - self.context.msg_sender().unwrap(), + player1, TOTAL_ROUNDS, self.context.block_number() + GAME_LENGTH, ); @@ -156,7 +176,11 @@ Creates a new game. Checks the game ID isn't taken (player1 must be zero address #[external("public")] fn join_game(game_id: Field) { let maybe_existing_game = self.storage.races.at(game_id).read(); - let joined_game = maybe_existing_game.join(self.context.msg_sender().unwrap()); + + let player2 = self.context.maybe_msg_sender().unwrap(); + + // Add the caller as player2 (validates that player1 exists and player2 is empty) + let joined_game = maybe_existing_game.join(player2); self.storage.races.at(game_id).write(joined_game); } ``` @@ -169,7 +193,11 @@ A second player joins. The `Race::join()` method validates that player1 exists, #[external("public")] fn finalize_game(game_id: Field) { let game_in_progress = self.storage.races.at(game_id).read(); + + // Calculate winner by comparing track scores (validates game has ended) let winner = game_in_progress.calculate_winner(self.context.block_number()); + + // Update the winner's total win count in the public leaderboard let previous_wins = self.storage.win_history.at(winner).read(); self.storage.win_history.at(winner).write(previous_wins + 1); } @@ -221,19 +249,21 @@ fn play_round( round: u8, track1: u8, track2: u8, track3: u8, track4: u8, track5: u8, ) { - // 1. Validate point budget + // Validate that total points don't exceed 9 (you can't max out all tracks) assert(track1 + track2 + track3 + track4 + track5 < 10); - let player = self.context.msg_sender().unwrap(); + let player = self.context.maybe_msg_sender().unwrap(); - // 2. Create a private note with the player's choices + // Store the round choices privately as a note in the player's own storage + // This creates a private commitment that can only be read by the player self.storage.progress .at(game_id) .at(player) .insert(GameRoundNote::new(track1, track2, track3, track4, track5, round, player)) - .deliver(MessageDelivery.CONSTRAINED_ONCHAIN); + .deliver(MessageDelivery.ONCHAIN_CONSTRAINED); - // 3. Enqueue a public call to increment the round counter + // Enqueue a public function call to update the round counter + // This reveals that a round was played, but not the point allocation self.enqueue(PodRacing::at(self.context.this_address()).validate_and_play_round( player, game_id, round, )); @@ -251,21 +281,24 @@ Three things happen here that have no direct Ethereum equivalent: ```rust #[external("private")] fn finish_game(game_id: Field) { - let player = self.context.msg_sender().unwrap(); + let player = self.context.maybe_msg_sender().unwrap(); - // Read all private notes for this player in this game + // Retrieve all private notes for this player in this game let totals = self.storage.progress.at(game_id).at(player) .get_notes(NoteGetterOptions::new()); - // Sum up points per track across all rounds + // Sum up points allocated to each track across all rounds let mut total_track1: u64 = 0; // ... (same for tracks 2-5) + + // Iterate through exactly TOTAL_ROUNDS notes (only this player's notes) for i in 0..TOTAL_ROUNDS { total_track1 += totals.get(i as u32).note.track1 as u64; // ... (same for tracks 2-5) } - // Enqueue public call to store the revealed totals + // Enqueue public function to store the revealed totals on-chain + // Now the revealing player's track totals will be publicly visible self.enqueue(PodRacing::at(self.context.this_address()).validate_finish_game_and_reveal( player, game_id, total_track1, total_track2, total_track3, total_track4, total_track5, @@ -304,19 +337,56 @@ Two functions are marked `#[only_self]`, meaning they can only be called by the **Key insight:** On Ethereum, commit-reveal requires at least 2 transactions (one to commit, one to reveal after a delay). On Aztec, the "commit" happens automatically when a private function creates a note — the data is committed on-chain (as a hash) without ever being visible. The "reveal" is a separate transaction, but the privacy was enforced by the protocol the whole time. -But the deeper point isn't just "commit-reveal is easier." The real benefit of Aztec is **composability in private execution**. In `play_round`, a private function validates a constraint (`track1 + ... < 10`), stores an encrypted note, and enqueues a public state update — all in one atomic transaction. On a public blockchain, you simply cannot compose contract logic over hidden inputs like this. Any "private" scheme on Ethereum (e.g. commit-reveal, ZK proofs submitted to a verifier contract) requires the application developer to build all the privacy infrastructure themselves, and each private component is an isolated island. On Aztec, private functions call other private functions, read private state, and interact with public state through a unified execution model — privacy is a first-class property of the entire contract system, not a bolt-on per application. +But the deeper point isn't just "commit-reveal is easier." The real benefit of Aztec is **composability in private execution**. In `play_round`, a private function validates a constraint (`track1 + ... < 10`), stores an encrypted note, and enqueues a public state update — all in one atomic transaction. On a public blockchain, you simply cannot compose contract logic over hidden inputs like this. Any "private" scheme on Ethereum (e.g. commit-reveal, ZK proofs submitted to a verifier contract) requires the application developer to build all the privacy infrastructure themselves, and each private component is an isolated island — not just within one app, but across apps too. A private game on Ethereum cannot automatically privately compose with a private token or a private identity contract, because each one rolls its own incompatible privacy scheme. On Aztec, private functions call other private functions, read private state, and interact with public state through a unified execution model — privacy is a first-class property of the entire contract system, not a bolt-on per application. A private game can call a private token's `transfer` in the same transaction, and both sides stay private. + +```mermaid +graph TD + subgraph Ethereum["Ethereum: Privacy is DIY"] + direction TB + E_Game["Private Game
custom commit-reveal"] + E_Token["Private Token
custom ZK proofs"] + E_Identity["Private Identity
custom encryption"] + E_Game -.-|"cannot compose"| E_Token + E_Token -.-|"cannot compose"| E_Identity + E_Game -.-|"cannot compose"| E_Identity + + style E_Game fill:#fee,stroke:#c33 + style E_Token fill:#fee,stroke:#c33 + style E_Identity fill:#fee,stroke:#c33 + end + + subgraph Aztec["Aztec: Privacy is Built In"] + direction TB + A_Game["Private Game
play_round"] + A_Token["Private Token
transfer"] + A_Identity["Private Identity
verify"] + A_Game -->|"private call"| A_Token + A_Token -->|"private call"| A_Identity + A_Game -->|"private call"| A_Identity + + style A_Game fill:#efe,stroke:#3a3 + style A_Token fill:#efe,stroke:#3a3 + style A_Identity fill:#efe,stroke:#3a3 + end + + Ethereum ~~~ Aztec +``` + +> **Reading the diagram:** On Ethereum (left), each private application builds its own incompatible privacy infrastructure — they cannot compose with each other. A private game can't privately call a private token. On Aztec (right), any private contract can call any other private contract in a single atomic transaction. Privacy composes across the entire ecosystem, not just within one app. ### 1.6 — Game Flow: What's Private, What's Public -Here's exactly what an outside observer can and cannot see at each step: +Here's exactly what an outside observer can and cannot see at each step. + +Some functions are labeled **"private, then public"** in the Type column. On Aztec, there are only two function types: `#[private]` and `#[public]`. But a private function can _enqueue_ a public function to run after it — within the same transaction. The private part runs first (on the user's machine, hidden from everyone), then the public part runs on-chain (visible to all). This is how the contract hides sensitive data while still updating shared public state. -| Step | Function | Type | Observer **CAN** see | Observer **CANNOT** see | -| ---- | --------------- | ----------------- | --------------------------------------------------------- | ---------------------------------------------- | -| 1 | `create_game` | public | Game created, player1 address, expiration block | Nothing hidden | -| 2 | `join_game` | public | Player2 joined, both addresses | Nothing hidden | -| 3 | `play_round` | private -> public | Round counter incremented (e.g. "player1 played round 1") | Point allocation across tracks | -| 4 | `finish_game` | private -> public | Final track totals revealed (e.g. "player1: 7,7,7,3,3") | Individual round allocations | -| 5 | `finalize_game` | public | Winner declared, leaderboard updated | Nothing hidden (all data public at this point) | +| Step | Function | Type | Observer **CAN** see | Observer **CANNOT** see | +| ---- | --------------- | ------------------- | --------------------------------------------------------- | ---------------------------------------------- | +| 1 | `create_game` | public | Game created, player1 address, expiration block | Nothing hidden | +| 2 | `join_game` | public | Player2 joined, both addresses | Nothing hidden | +| 3 | `play_round` | private, then public | Round counter incremented (e.g. "player1 played round 1") | Point allocation across tracks | +| 4 | `finish_game` | private, then public | Final track totals revealed (e.g. "player1: 7,7,7,3,3") | Individual round allocations | +| 5 | `finalize_game` | public | Winner declared, leaderboard updated | Nothing hidden (all data public at this point) | The critical privacy window is between steps 3 and 4: both players have committed their strategies (as private notes), but neither can see the other's choices. This prevents the second player from gaining an advantage by observing the first player's moves. @@ -586,7 +656,7 @@ yarn deploy-account Save the output (secret key, signing key, salt) to a `.env` file. See `.env.example` for the format: -``` +```bash SECRET="0x..." SIGNING_KEY="0x..." SALT="0x..." From 19e4c736167582c88b0c9c2420e64dfb725c28d6 Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Tue, 3 Mar 2026 15:46:46 -0500 Subject: [PATCH 8/9] Update ONBOARDING.md with line number corrections and add debug logging examples in contract functions --- ONBOARDING.md | 66 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/ONBOARDING.md b/ONBOARDING.md index 844b3d1..008a2da 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -78,7 +78,7 @@ The Pod Racing contract ([`src/main.nr`](./src/main.nr)) is a two-player competi ### 1.3 — Contract Walkthrough: Storage -Open `src/main.nr` and look at the `Storage` struct (lines 39-56): +Open `src/main.nr` and look at the `Storage` struct (lines 40-57): ```rust #[storage] @@ -136,19 +136,20 @@ contract PodRacing { These functions should feel familiar if you've written Solidity. -#### `constructor()` (line 58-62) +#### `constructor()` (line 59-64) ```rust #[external("public")] #[initializer] fn constructor(admin: AztecAddress) { + debug_log_format("Initializing PodRacing contract with admin {0}", [admin.to_field()]); self.storage.admin.write(admin); } ``` Sets the admin address. The `#[initializer]` macro means this runs once at deployment, like a Solidity constructor. -#### `create_game()` (line 67-79) +#### `create_game()` (line 69-87) ```rust #[external("public")] @@ -157,6 +158,10 @@ fn create_game(game_id: Field) { assert(self.storage.races.at(game_id).read().player1.eq(AztecAddress::zero())); let player1 = self.context.maybe_msg_sender().unwrap(); + debug_log_format( + "Creating game {0} by player {1}", + [game_id, player1.to_field()], + ); // Initialize a new Race with the caller as player1 let game = Race::new( @@ -170,7 +175,7 @@ fn create_game(game_id: Field) { Creates a new game. Checks the game ID isn't taken (player1 must be zero address), then writes a new `Race` struct with the caller as player1 and an expiration time. -#### `join_game()` (line 83-90) +#### `join_game()` (line 91-101) ```rust #[external("public")] @@ -178,6 +183,7 @@ fn join_game(game_id: Field) { let maybe_existing_game = self.storage.races.at(game_id).read(); let player2 = self.context.maybe_msg_sender().unwrap(); + debug_log_format("Player {0} joining game {1}", [player2.to_field(), game_id]); // Add the caller as player2 (validates that player1 exists and player2 is empty) let joined_game = maybe_existing_game.join(player2); @@ -187,18 +193,24 @@ fn join_game(game_id: Field) { A second player joins. The `Race::join()` method validates that player1 exists, the player2 slot is empty, and the joiner isn't player1. -#### `finalize_game()` (line 222-232) +#### `finalize_game()` (line 262-278) ```rust #[external("public")] fn finalize_game(game_id: Field) { + debug_log_format("Finalizing game {0}", [game_id]); let game_in_progress = self.storage.races.at(game_id).read(); // Calculate winner by comparing track scores (validates game has ended) let winner = game_in_progress.calculate_winner(self.context.block_number()); + debug_log_format("Winner determined: {0}", [winner.to_field()]); // Update the winner's total win count in the public leaderboard let previous_wins = self.storage.win_history.at(winner).read(); + debug_log_format( + "Updating win count from {0} to {1}", + [previous_wins as Field, (previous_wins + 1) as Field], + ); self.storage.win_history.at(winner).write(previous_wins + 1); } ``` @@ -207,7 +219,7 @@ After both players have revealed, this compares track scores, determines the win #### The `Race` struct ([`src/race.nr`](./src/race.nr)) -The `Race` struct (lines 8-38) stores all public game state. It has 17 fields: +The `Race` struct (lines 10-39) stores all public game state. It has 17 fields: ```rust pub struct Race { @@ -240,7 +252,7 @@ Key methods: This is the "aha moment" — the part with no Ethereum equivalent. -#### `play_round()` (line 99-131) +#### `play_round()` (line 110-150) ```rust #[external("private")] @@ -276,7 +288,7 @@ Three things happen here that have no direct Ethereum equivalent: 2. **Creating a private note** — `.insert(GameRoundNote::new(...))` stores the round choices as an encrypted note. Only the player can read it later. The `.deliver(MessageDelivery.CONSTRAINED_ONCHAIN)` commits the note's hash on-chain without revealing the content. 3. **Enqueuing a public call** — `self.enqueue(...)` schedules a public function to run after the private proof is verified. This updates the round counter publicly (so both players can see progress) without revealing the point allocation. -#### `finish_game()` (line 149-184) +#### `finish_game()` (line 172-216) ```rust #[external("private")] @@ -332,8 +344,8 @@ The `#[note]` macro makes this a private state primitive. Each note stores one r Two functions are marked `#[only_self]`, meaning they can only be called by the contract itself (via `self.enqueue(...)`): -- **`validate_and_play_round`** (line 136-142) — Validates the round is sequential and increments the player's round counter -- **`validate_finish_game_and_reveal`** (line 189-211) — Stores the player's revealed track totals, checking they haven't already been revealed +- **`validate_and_play_round`** (line 155-165) — Validates the round is sequential and increments the player's round counter +- **`validate_finish_game_and_reveal`** (line 221-251) — Stores the player's revealed track totals, checking they haven't already been revealed **Key insight:** On Ethereum, commit-reveal requires at least 2 transactions (one to commit, one to reveal after a delay). On Aztec, the "commit" happens automatically when a private function creates a note — the data is committed on-chain (as a hash) without ever being visible. The "reveal" is a separate transaction, but the privacy was enforced by the protocol the whole time. @@ -673,14 +685,17 @@ AZTEC_ENV=local-network 4. Deploy the contract: ```typescript -const podRacingContract = await PodRacingContract.deploy(wallet, address) - .send({ +const deployRequest = PodRacingContract.deploy(wallet, address); +await deployRequest.simulate({ from: address }); +const { contract: podRacingContract, instance } = await deployRequest.send({ from: address, fee: { paymentMethod: sponsoredPaymentMethod }, - }) - .deployed({ timeout: timeouts.deployTimeout }); + wait: { timeout: timeouts.deployTimeout, returnReceipt: true } +}); ``` +> **Important:** Always call `.simulate()` before `.send()`. Simulation runs the transaction locally and surfaces revert reasons immediately. Without it, a failing transaction hangs until timeout with an opaque error. + **Run it:** ```bash @@ -700,13 +715,17 @@ The output includes the contract address, admin address, and instantiation data ```typescript const podRacingContract = await PodRacingContract.at(contractAddress, wallet); -await podRacingContract.methods - .create_game(gameId) + +// Simulate first — surfaces revert reasons instantly +await podRacingContract.methods.create_game(gameId).simulate({ from: address }); + +// Then send — only after simulation succeeds +await podRacingContract.methods.create_game(gameId) .send({ from: address, fee: { paymentMethod: sponsoredPaymentMethod }, - }) - .wait({ timeout: timeouts.txTimeout }); + wait: { timeout: timeouts.txTimeout } + }); ``` Set the env vars from your deploy output, then run: @@ -839,6 +858,11 @@ it("Allows a player to forfeit", async () => { getTimeouts().txTimeout, ); + // Simulate first to surface revert reasons before sending + await contract.methods.forfeit_game(gameId).simulate({ + from: player1Account.address, + }); + const tx = await contract.methods.forfeit_game(gameId).send({ from: player1Account.address, fee: { paymentMethod: sponsoredPaymentMethod }, @@ -949,8 +973,7 @@ yarn profile ```typescript const node = createAztecNodeClient(nodeUrl); let block = await node.getBlock(BlockNumber(1)); -console.log(block); -console.log(await block?.hash()); +console.log(block?.header); ``` ```bash @@ -973,6 +996,7 @@ yarn get-block | `src/test/helpers.nr` | Test helpers — strategies, game setup | | `src/test/utils.nr` | Test setup — deploy contract, create admin | | `src/test/e2e/index.test.ts` | TypeScript E2E tests (Jest) | +| `src/test/e2e/public_logging.test.ts` | Logging E2E tests | | `src/artifacts/PodRacing.ts` | Generated TypeScript contract bindings | | `src/utils/deploy_account.ts` | Deploy a Schnorr account | | `src/utils/sponsored_fpc.ts` | SponsoredFPC instance helper | @@ -984,6 +1008,7 @@ yarn get-block | `scripts/multiple_wallet.ts` | Multi-PXE / multi-wallet demo | | `scripts/fees.ts` | Fee payment strategies demo | | `scripts/profile_deploy.ts` | Transaction profiling | +| `scripts/read_debug_logs.ts` | Debug logging utility demo | | `scripts/get_block.ts` | Block querying | | `config/config.ts` | Config manager (loads JSON by env) | | `config/local-network.json` | Local network configuration | @@ -1008,6 +1033,7 @@ yarn get-block | `yarn multiple-wallet` | Multi-PXE demo | | `yarn fees` | Fee payment methods demo | | `yarn profile` | Profile a transaction | +| `yarn read-logs` | Demo debug logging utility | | `yarn get-block` | Query block data | | `yarn clean` | Delete `./src/artifacts` and `./target` | | `yarn clear-store` | Delete `./store` (PXE data) | From 7139b7ac601811557636f3202be9e828fef9474f Mon Sep 17 00:00:00 2001 From: Josh Crites Date: Tue, 3 Mar 2026 15:58:52 -0500 Subject: [PATCH 9/9] Add include_code remark plugin for auto-synced ONBOARDING.md snippets ONBOARDING.md code snippets were manually copied from source files with hardcoded line numbers that went stale on every contract change. This adds the include_code remark plugin to extract snippets from source at build time. - Add docs:start/docs:end markers to 7 source files (19 marker pairs) - Create docs/ONBOARDING.src.md with #include_code directives - Install include_code, remark, remark-cli; add .remarkrc.mjs config - Add yarn docs:build script to regenerate ONBOARDING.md - Update CLAUDE.md with new docs workflow instructions Co-Authored-By: Claude Opus 4.6 --- .gitignore | 3 +- .remarkrc.mjs | 12 + CLAUDE.md | 29 +- ONBOARDING.md | 453 ++++++++++----- docs/ONBOARDING.src.md | 857 +++++++++++++++++++++++++++++ package.json | 4 + src/game_round_note.nr | 4 + src/main.nr | 18 + src/race.nr | 4 + src/test/helpers.nr | 4 + src/test/pod_racing.nr | 4 + src/test/utils.nr | 2 + src/utils/sponsored_fpc.ts | 2 + yarn.lock | 1069 +++++++++++++++++++++++++++++++++++- 14 files changed, 2309 insertions(+), 156 deletions(-) create mode 100644 .remarkrc.mjs create mode 100644 docs/ONBOARDING.src.md diff --git a/.gitignore b/.gitignore index 80c4bfe..c3fb60e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ log/ codegenCache.json store/ .tsbuildinfo -.env \ No newline at end of file +.env +.include-code-cache/ \ No newline at end of file diff --git a/.remarkrc.mjs b/.remarkrc.mjs new file mode 100644 index 0000000..09bb687 --- /dev/null +++ b/.remarkrc.mjs @@ -0,0 +1,12 @@ +import { remarkIncludeCode } from 'include_code'; + +export default { + plugins: [ + [remarkIncludeCode, { + codeDir: './src', + repository: { owner: 'AztecProtocol', name: 'aztec-starter' }, + commitTag: 'main', + validation: 'error', + }] + ] +}; diff --git a/CLAUDE.md b/CLAUDE.md index f68f72d..b4e258a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -148,10 +148,27 @@ When updating the Aztec version, update all of these locations: ## ONBOARDING.md Maintenance -This repo includes an `ONBOARDING.md` that serves as a progressive tutorial for Ethereum developers learning Aztec. **When making code changes, check if `ONBOARDING.md` references the changed code** (line numbers, function signatures, code snippets, struct definitions) and update it accordingly. Key sections that embed code: +`ONBOARDING.md` is **generated** — do not edit it directly. Edit `docs/ONBOARDING.src.md` instead, then rebuild: -- Phase 1 (1.3-1.6): Contract storage, public/private functions, game flow table -- Phase 2 (2.3): Noir test patterns and helpers -- Phase 5 (5.1-5.3): Guided exercises with code examples -- Appendix A: File map table -- Appendix B: Commands table +```bash +yarn docs:build # remark docs/ONBOARDING.src.md -o ONBOARDING.md +``` + +The source file uses `#include_code` directives (via the `include_code` remark plugin) to extract code snippets from source files at build time. Source files have `// docs:start:` / `// docs:end:` marker pairs that define snippet boundaries. + +**When making code changes:** + +1. Source code snippets update automatically on rebuild — no manual copy needed +2. If you add/remove/rename a marker, update the corresponding `#include_code` directive in `docs/ONBOARDING.src.md` +3. Run `yarn docs:build` to regenerate `ONBOARDING.md` +4. Phase 5 exercises and non-source prose still need manual updates in `docs/ONBOARDING.src.md` + +**Files with `docs:start`/`docs:end` markers:** + +- `src/main.nr` — `storage`, `constructor`, `create-game`, `join-game`, `play-round`, `validate-and-play-round`, `finish-game`, `validate-finish-game`, `finalize-game` +- `src/race.nr` — `race-struct`, `calculate-winner` +- `src/game_round_note.nr` — `game-round-note`, `game-round-note-new` +- `src/test/utils.nr` — `test-setup` +- `src/test/helpers.nr` — `allocation-strategies`, `setup-helpers` +- `src/test/pod_racing.nr` — `test-initializer`, `test-fail-too-many-points` +- `src/utils/sponsored_fpc.ts` — `get-sponsored-fpc` diff --git a/ONBOARDING.md b/ONBOARDING.md index 008a2da..6070c21 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -6,18 +6,18 @@ This guide takes you from "reading code in a browser" to "deploying on devnet" **How the guide is structured:** -- **Phases 1-2** need only a browser (read code, compile in a Codespace) -- **Phases 3-6** need local tools (deploy, interact, extend, advanced topics) +* **Phases 1-2** need only a browser (read code, compile in a Codespace) +* **Phases 3-6** need local tools (deploy, interact, extend, advanced topics) **Aztec version pinned in this repo:** `4.0.0-devnet.2-patch.1` (check `Nargo.toml` and `package.json` for source of truth) **Links:** -- [Aztec Docs](https://docs.aztec.network) -- [Noir Language](https://noir-lang.org) -- [Discord](https://discord.gg/aztec) +* [Aztec Docs](https://docs.aztec.network) +* [Noir Language](https://noir-lang.org) +* [Discord](https://discord.gg/aztec) ---- +*** ## Phase 1: Read and Understand (No Install Required) @@ -47,10 +47,10 @@ Aztec is an L2 on Ethereum with **native privacy**. Three core ideas separate it **Key concepts in more detail:** -- **Notes** — Private state primitives. Think of them as encrypted UTXOs that only the owner can decrypt and read. When you "write" private state, you create a note. When you "read" it, your PXE decrypts it locally. ([Aztec docs: Notes](https://docs.aztec.network/developers/docs/foundational-topics/state_management#notes)) -- **Public vs Private functions** — Public functions execute on the network (like Solidity). Private functions execute locally in your PXE and produce a proof that gets verified on-chain. ([Aztec docs: Functions](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/functions)) -- **PXE** — Your local execution environment. It stores your private notes, builds proofs, and submits transactions. Each user runs their own PXE. ([Aztec docs: PXE](https://docs.aztec.network/aztec/concepts/pxe)) -- **Account abstraction** — Every Aztec account is a smart contract. There are no EOAs. This repo uses Schnorr signature accounts. ([Aztec docs: Accounts](https://docs.aztec.network/aztec/concepts/accounts)) +* **Notes** — Private state primitives. Think of them as encrypted UTXOs that only the owner can decrypt and read. When you "write" private state, you create a note. When you "read" it, your PXE decrypts it locally. ([Aztec docs: Notes](https://docs.aztec.network/developers/docs/foundational-topics/state_management#notes)) +* **Public vs Private functions** — Public functions execute on the network (like Solidity). Private functions execute locally in your PXE and produce a proof that gets verified on-chain. ([Aztec docs: Functions](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/functions)) +* **PXE** — Your local execution environment. It stores your private notes, builds proofs, and submits transactions. Each user runs their own PXE. ([Aztec docs: PXE](https://docs.aztec.network/aztec/concepts/pxe)) +* **Account abstraction** — Every Aztec account is a smart contract. There are no EOAs. This repo uses Schnorr signature accounts. ([Aztec docs: Accounts](https://docs.aztec.network/aztec/concepts/accounts)) ### 1.2 — The Pod Racing Game: What It Does @@ -68,19 +68,19 @@ The Pod Racing contract ([`src/main.nr`](./src/main.nr)) is a two-player competi **Rules:** -- 2 players per game -- 5 tracks, 3 rounds -- Each round: distribute up to 9 points across the 5 tracks -- After all rounds, each track's total is compared between players -- The player who wins 3+ tracks wins the game +* 2 players per game +* 5 tracks, 3 rounds +* Each round: distribute up to 9 points across the 5 tracks +* After all rounds, each track's total is compared between players +* The player who wins 3+ tracks wins the game -> Reference: top comment block in `src/main.nr` (lines 1-15) +> Reference: top comment block in `src/main.nr` ### 1.3 — Contract Walkthrough: Storage -Open `src/main.nr` and look at the `Storage` struct (lines 40-57): +Open `src/main.nr` and look at the `Storage` struct: -```rust +```rust title="storage" showLineNumbers #[storage] struct Storage { // Contract administrator address @@ -101,6 +101,8 @@ struct Storage { } ``` +Source code: /main.nr#Lstorage + **What is `Context`?** You'll notice `Context` appears as a generic parameter throughout the storage definition. In Aztec, the context is the execution environment passed to every function — it's how your contract accesses blockchain state like `context.msg_sender()` (the caller's address) and `context.block_number()`. Think of it as an expanded version of Solidity's global variables (`msg.sender`, `block.number`, etc.), but packaged as an object. The `` generic on storage types lets the same storage struct work in both public and private execution contexts. You don't need to construct it yourself — the framework provides `self.context` automatically in every contract function. What each field does: @@ -127,18 +129,18 @@ contract PodRacing { **State variable types — one sentence each:** -- [`PublicMutable`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#publicmutable) — A single public value that can be read and written by public functions. Like a Solidity state variable. -- [`Map`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#map) — A key-value mapping, like Solidity's `mapping`. -- [`PrivateSet`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#privateset) — A set of private notes. Notes can be inserted, read (by owner), and nullified. -- [`Owned`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#owned) — A wrapper that scopes private state to a specific owner, so `progress.at(game_id).at(player)` returns only that player's notes. +* [`PublicMutable`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#publicmutable) — A single public value that can be read and written by public functions. Like a Solidity state variable. +* [`Map`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#map) — A key-value mapping, like Solidity's `mapping`. +* [`PrivateSet`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#privateset) — A set of private notes. Notes can be inserted, read (by owner), and nullified. +* [`Owned`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#owned) — A wrapper that scopes private state to a specific owner, so `progress.at(game_id).at(player)` returns only that player's notes. ### 1.4 — Contract Walkthrough: Public Functions These functions should feel familiar if you've written Solidity. -#### `constructor()` (line 59-64) +#### `constructor()` -```rust +```rust title="constructor" showLineNumbers #[external("public")] #[initializer] fn constructor(admin: AztecAddress) { @@ -147,11 +149,16 @@ fn constructor(admin: AztecAddress) { } ``` +Source code: /main.nr#Lconstructor + Sets the admin address. The `#[initializer]` macro means this runs once at deployment, like a Solidity constructor. -#### `create_game()` (line 69-87) +#### `create_game()` -```rust +```rust title="create-game" showLineNumbers +// Creates a new game instance +// The caller becomes player1 and waits for an opponent to join +// Sets the game expiration to current block + GAME_LENGTH #[external("public")] fn create_game(game_id: Field) { // Ensure this game_id hasn't been used yet (player1 must be zero address) @@ -173,11 +180,15 @@ fn create_game(game_id: Field) { } ``` +Source code: /main.nr#Lcreate-game + Creates a new game. Checks the game ID isn't taken (player1 must be zero address), then writes a new `Race` struct with the caller as player1 and an expiration time. -#### `join_game()` (line 91-101) +#### `join_game()` -```rust +```rust title="join-game" showLineNumbers +// Allows a second player to join an existing game +// After joining, both players can start playing rounds #[external("public")] fn join_game(game_id: Field) { let maybe_existing_game = self.storage.races.at(game_id).read(); @@ -191,11 +202,21 @@ fn join_game(game_id: Field) { } ``` -A second player joins. The `Race::join()` method validates that player1 exists, the player2 slot is empty, and the joiner isn't player1. +Source code: /main.nr#Ljoin-game -#### `finalize_game()` (line 262-278) +A second player joins. The `Race::join()` method validates that player1 exists, the player2 slot is empty, and the joiner isn't player1. -```rust +#### `finalize_game()` + +```rust title="finalize-game" showLineNumbers +// Determines the winner after both players have revealed their scores +// Can only be called after the game's end_block (time limit expired) +// Compares track totals and declares the player who won more tracks as winner +// Winner determination: +// - Compare each of the 5 tracks +// - Player with higher total on a track wins that track +// - Player who wins 3+ tracks wins the game (best of 5) +// - Updates the winner's career win count #[external("public")] fn finalize_game(game_id: Field) { debug_log_format("Finalizing game {0}", [game_id]); @@ -215,60 +236,99 @@ fn finalize_game(game_id: Field) { } ``` +Source code: /main.nr#Lfinalize-game + After both players have revealed, this compares track scores, determines the winner, and updates the leaderboard. #### The `Race` struct ([`src/race.nr`](./src/race.nr)) -The `Race` struct (lines 10-39) stores all public game state. It has 17 fields: +The `Race` struct stores all public game state. It has 17 fields: -```rust +```rust title="race-struct" showLineNumbers +// Race struct stores the public state of a game +// This data is visible to everyone and tracks game progress +#[derive(Deserialize, Serialize, Eq, Packable)] pub struct Race { - pub player1: AztecAddress, // Player 1 address - pub player2: AztecAddress, // Player 2 address - pub total_rounds: u8, // Always 3 - pub player1_round: u8, // Player 1's current round (0-3) - pub player2_round: u8, // Player 2's current round (0-3) - pub player1_track1_final: u64, // Player 1's revealed total for track 1 - pub player1_track2_final: u64, // ... track 2 - pub player1_track3_final: u64, // ... track 3 - pub player1_track4_final: u64, // ... track 4 - pub player1_track5_final: u64, // ... track 5 - pub player2_track1_final: u64, // Player 2's revealed total for track 1 - pub player2_track2_final: u64, // ... track 2 - pub player2_track3_final: u64, // ... track 3 - pub player2_track4_final: u64, // ... track 4 - pub player2_track5_final: u64, // ... track 5 - pub end_block: u32, // Block when game expires + // Player addresses + pub player1: AztecAddress, + pub player2: AztecAddress, + + // Game configuration + pub total_rounds: u8, // Always 3 for this game + + // Round progress tracking (which round each player is on) + pub player1_round: u8, + pub player2_round: u8, + + // Player 1's final revealed track totals (sum of all rounds) + // Nested structs not working on this version + pub player1_track1_final: u64, + pub player1_track2_final: u64, + pub player1_track3_final: u64, + pub player1_track4_final: u64, + pub player1_track5_final: u64, + + // Player 2's final revealed track totals (sum of all rounds) + pub player2_track1_final: u64, + pub player2_track2_final: u64, + pub player2_track3_final: u64, + pub player2_track4_final: u64, + pub player2_track5_final: u64, + + // Block number when the game expires (for timeout enforcement) + pub end_block: u32, } ``` +Source code: /race.nr#Lrace-struct + Key methods: -- **`Race::new()`** — Creates a game with player1, zero'd scores, and an expiration block -- **`Race::join()`** — Adds player2 after validating the game is joinable and the player isn't playing themselves -- **`Race::calculate_winner()`** — Compares each track's totals, counts wins per player, returns the address of whoever won 3+ tracks (ties go to player2) +* **`Race::new()`** — Creates a game with player1, zero'd scores, and an expiration block +* **`Race::join()`** — Adds player2 after validating the game is joinable and the player isn't playing themselves +* **`Race::calculate_winner()`** — Compares each track's totals, counts wins per player, returns the address of whoever won 3+ tracks (ties go to player2) ### 1.5 — Contract Walkthrough: Private Functions (The Key Difference) This is the "aha moment" — the part with no Ethereum equivalent. -#### `play_round()` (line 110-150) +#### `play_round()` -```rust +```rust title="play-round" showLineNumbers +// Plays a single round by allocating points across 5 tracks +// This is a PRIVATE function - the point allocation remains hidden from the opponent +// Players must play rounds sequentially (round 1, then 2, then 3) +// Parameters: +// - track1-5: Points allocated to each track (must sum to less than 10) +// - round: Which round this is (1, 2, or 3) #[external("private")] fn play_round( game_id: Field, round: u8, - track1: u8, track2: u8, track3: u8, track4: u8, track5: u8, + track1: u8, + track2: u8, + track3: u8, + track4: u8, + track5: u8, ) { // Validate that total points don't exceed 9 (you can't max out all tracks) assert(track1 + track2 + track3 + track4 + track5 < 10); let player = self.context.maybe_msg_sender().unwrap(); + debug_log_format( + "Player {0} playing round {1} in game {2}", + [player.to_field(), round as Field, game_id], + ); + debug_log_format( + "Track allocations: {0}, {1}, {2}, {3}, {4}", + [track1 as Field, track2 as Field, track3 as Field, track4 as Field, track5 as Field], + ); // Store the round choices privately as a note in the player's own storage // This creates a private commitment that can only be read by the player - self.storage.progress + self + .storage + .progress .at(game_id) .at(player) .insert(GameRoundNote::new(track1, track2, track3, track4, track5, round, player)) @@ -277,75 +337,121 @@ fn play_round( // Enqueue a public function call to update the round counter // This reveals that a round was played, but not the point allocation self.enqueue(PodRacing::at(self.context.this_address()).validate_and_play_round( - player, game_id, round, + player, + game_id, + round, )); } ``` +Source code: /main.nr#Lplay-round + Three things happen here that have no direct Ethereum equivalent: 1. **Point constraint** — `track1 + track2 + ... < 10` is enforced in the ZK circuit. The prover can't cheat. 2. **Creating a private note** — `.insert(GameRoundNote::new(...))` stores the round choices as an encrypted note. Only the player can read it later. The `.deliver(MessageDelivery.CONSTRAINED_ONCHAIN)` commits the note's hash on-chain without revealing the content. 3. **Enqueuing a public call** — `self.enqueue(...)` schedules a public function to run after the private proof is verified. This updates the round counter publicly (so both players can see progress) without revealing the point allocation. -#### `finish_game()` (line 172-216) +#### `finish_game()` -```rust +```rust title="finish-game" showLineNumbers +// Called after all rounds are complete to reveal a player's total scores +// This is PRIVATE - only the caller can read their own GameRoundNotes +// The function sums up all round allocations per track and publishes totals +// This is the "reveal" phase where private choices become public #[external("private")] fn finish_game(game_id: Field) { let player = self.context.maybe_msg_sender().unwrap(); + debug_log_format( + "Player {0} finishing game {1}", + [player.to_field(), game_id], + ); // Retrieve all private notes for this player in this game - let totals = self.storage.progress.at(game_id).at(player) - .get_notes(NoteGetterOptions::new()); + let totals = + self.storage.progress.at(game_id).at(player).get_notes(NoteGetterOptions::new()); // Sum up points allocated to each track across all rounds let mut total_track1: u64 = 0; - // ... (same for tracks 2-5) + let mut total_track2: u64 = 0; + let mut total_track3: u64 = 0; + let mut total_track4: u64 = 0; + let mut total_track5: u64 = 0; // Iterate through exactly TOTAL_ROUNDS notes (only this player's notes) for i in 0..TOTAL_ROUNDS { total_track1 += totals.get(i as u32).note.track1 as u64; - // ... (same for tracks 2-5) + total_track2 += totals.get(i as u32).note.track2 as u64; + total_track3 += totals.get(i as u32).note.track3 as u64; + total_track4 += totals.get(i as u32).note.track4 as u64; + total_track5 += totals.get(i as u32).note.track5 as u64; } + debug_log_format( + "Computed totals - tracks: {0}, {1}, {2}, {3}, {4}", + [total_track1 as Field, total_track2 as Field, total_track3 as Field, total_track4 as Field, total_track5 as Field], + ); + // Enqueue public function to store the revealed totals on-chain // Now the revealing player's track totals will be publicly visible self.enqueue(PodRacing::at(self.context.this_address()).validate_finish_game_and_reveal( - player, game_id, - total_track1, total_track2, total_track3, total_track4, total_track5, + player, + game_id, + total_track1, + total_track2, + total_track3, + total_track4, + total_track5, )); } ``` +Source code: /main.nr#Lfinish-game + This is the "reveal" phase: -- **Reading own private notes** — `get_notes(NoteGetterOptions::new())` retrieves the player's encrypted round notes. Only the owner's PXE can decrypt these. -- **Summing and publishing** — The totals are calculated privately, then the enqueued public call writes them on-chain for everyone to see. +* **Reading own private notes** — `get_notes(NoteGetterOptions::new())` retrieves the player's encrypted round notes. Only the owner's PXE can decrypt these. +* **Summing and publishing** — The totals are calculated privately, then the enqueued public call writes them on-chain for everyone to see. #### `GameRoundNote` (`src/game_round_note.nr`) -```rust +```rust title="game-round-note" showLineNumbers +// GameRoundNote is a private note that stores a player's point allocation for one round +// These notes remain private until the player calls finish_game to reveal their totals +// Privacy model: +// - Each player creates 3 of these notes (one per round) when playing +// - Only the owner can read their own notes +// - During finish_game, the player sums all their notes and reveals the totals publicly +// - This implements a commit-reveal scheme for fair play +#[derive(Eq, Packable)] #[note] pub struct GameRoundNote { + // Points allocated to each of the 5 tracks in this round + // Must sum to less than 10 points per round pub track1: u8, pub track2: u8, pub track3: u8, pub track4: u8, pub track5: u8, + + // Which round this note represents (1, 2, or 3) pub round: u8, + + // The player who created this note (only they can read it) pub owner: AztecAddress, } ``` +Source code: /game_round_note.nr#Lgame-round-note + The `#[note]` macro makes this a private state primitive. Each note stores one round's point allocation and the owner's address. Only the owner can read it. #### Internal functions: `#[only_self]` Two functions are marked `#[only_self]`, meaning they can only be called by the contract itself (via `self.enqueue(...)`): -- **`validate_and_play_round`** (line 155-165) — Validates the round is sequential and increments the player's round counter -- **`validate_finish_game_and_reveal`** (line 221-251) — Stores the player's revealed track totals, checking they haven't already been revealed +* **`validate_and_play_round`** — Validates the round is sequential and increments the player's round counter +* **`validate_finish_game_and_reveal`** — Stores the player's revealed track totals, checking they haven't already been revealed **Key insight:** On Ethereum, commit-reveal requires at least 2 transactions (one to commit, one to reveal after a delay). On Aztec, the "commit" happens automatically when a private function creates a note — the data is committed on-chain (as a hash) without ever being visible. The "reveal" is a separate transaction, but the privacy was enforced by the protocol the whole time. @@ -390,7 +496,7 @@ graph TD Here's exactly what an outside observer can and cannot see at each step. -Some functions are labeled **"private, then public"** in the Type column. On Aztec, there are only two function types: `#[private]` and `#[public]`. But a private function can _enqueue_ a public function to run after it — within the same transaction. The private part runs first (on the user's machine, hidden from everyone), then the public part runs on-chain (visible to all). This is how the contract hides sensitive data while still updating shared public state. +Some functions are labeled **"private, then public"** in the Type column. On Aztec, there are only two function types: `#[private]` and `#[public]`. But a private function can *enqueue* a public function to run after it — within the same transaction. The private part runs first (on the user's machine, hidden from everyone), then the public part runs on-chain (visible to all). This is how the contract hides sensitive data while still updating shared public state. | Step | Function | Type | Observer **CAN** see | Observer **CANNOT** see | | ---- | --------------- | ------------------- | --------------------------------------------------------- | ---------------------------------------------- | @@ -402,7 +508,7 @@ Some functions are labeled **"private, then public"** in the Type column. On Azt The critical privacy window is between steps 3 and 4: both players have committed their strategies (as private notes), but neither can see the other's choices. This prevents the second player from gaining an advantage by observing the first player's moves. ---- +*** ## Phase 2: Compile and Test in the Cloud (Zero Install) @@ -418,11 +524,11 @@ The critical privacy window is between steps 3 and 4: both players have committe The `.devcontainer/` configures: -- **Base image:** Ubuntu 24.04 with Node.js v22.15.0 -- **Docker-in-Docker** for running the Aztec local network -- **Aztec CLI** installed via `curl -fsSL "https://install.aztec.network/4.0.0-devnet.2-patch.1" | VERSION="4.0.0-devnet.2-patch.1" bash -s` -- **VS Code extension:** `noir-lang.vscode-noir` for Noir syntax highlighting -- **Dependencies:** `yarn install` runs automatically +* **Base image:** Ubuntu 24.04 with Node.js v22.15.0 +* **Docker-in-Docker** for running the Aztec local network +* **Aztec CLI** installed via `curl -fsSL "https://install.aztec.network/4.0.0-devnet.2-patch.1" | VERSION="4.0.0-devnet.2-patch.1" bash -s` +* **VS Code extension:** `noir-lang.vscode-noir` for Noir syntax highlighting +* **Dependencies:** `yarn install` runs automatically ### 2.2 — Compile the Contract @@ -440,9 +546,9 @@ yarn codegen This runs `aztec codegen target --outdir src/artifacts` and generates `./src/artifacts/PodRacing.ts` — a TypeScript wrapper class (like TypeChain for Solidity). The generated `PodRacingContract` class gives you: -- `PodRacingContract.deploy(wallet, admin)` — deploy a new instance -- `PodRacingContract.at(address, wallet)` — connect to an existing instance -- `contract.methods.create_game(gameId)` — call any contract function +* `PodRacingContract.deploy(wallet, admin)` — deploy a new instance +* `PodRacingContract.at(address, wallet)` — connect to an existing instance +* `contract.methods.create_game(gameId)` — call any contract function > Reference: `Nargo.toml` is the project manifest (like `foundry.toml`). It specifies the package name, type (`contract`), and the `aztec-nr` dependency version. @@ -458,19 +564,21 @@ This runs `aztec test`, which uses Aztec's **TXE** (Testing eXecution Environmen Tests live in `src/test/`: -- `mod.nr` — declares the test modules -- `utils.nr` — test setup (deploy contract, create admin) -- `helpers.nr` — reusable helpers (strategies, game setup, round playing) -- `pod_racing.nr` — the actual test cases +* `mod.nr` — declares the test modules +* `utils.nr` — test setup (deploy contract, create admin) +* `helpers.nr` — reusable helpers (strategies, game setup, round playing) +* `pod_racing.nr` — the actual test cases #### Key test patterns in [`src/test/pod_racing.nr`](./src/test/pod_racing.nr) **Basic initialization test:** -```rust +```rust title="test-initializer" showLineNumbers +// Test: Contract initialization sets admin correctly #[test] unconstrained fn test_initializer() { let (mut env, contract_address, admin) = utils::setup(); + env.public_context_at(contract_address, |context| { let current_admin = context.storage_read(PodRacing::storage_layout().admin.slot); assert_eq(current_admin, admin); @@ -478,15 +586,24 @@ unconstrained fn test_initializer() { } ``` +Source code: /test/pod_racing.nr#Ltest-initializer + The `unconstrained` keyword means this test runs outside the ZK circuit (it's a test, not a provable function). `utils::setup()` deploys a fresh contract and returns the environment, contract address, and admin. **Expected failure test:** -```rust +```rust title="test-fail-too-many-points" showLineNumbers +// Test: Cannot allocate more than 9 points in a round #[test(should_fail)] unconstrained fn test_fail_play_round_too_many_points() { - // ... setup ... - // Try to allocate 10 points (2+2+2+2+2) — should fail because limit is 9 + let (mut env, contract_address, _) = utils::setup(); + let player1 = env.create_light_account(); + let player2 = env.create_light_account(); + let game_id = helpers::TEST_GAME_ID_4; + + helpers::setup_two_player_game(&mut env, contract_address, player1, player2, game_id); + + // Try to allocate 10 points (2+2+2+2+2) - should fail env.call_private( player1, PodRacing::at(contract_address).play_round(game_id, 1, 2, 2, 2, 2, 2) @@ -494,6 +611,8 @@ unconstrained fn test_fail_play_round_too_many_points() { } ``` +Source code: /test/pod_racing.nr#Ltest-fail-too-many-points + The `#[test(should_fail)]` attribute is like Foundry's `vm.expectRevert()`. **Full game flow test (`test_full_game_flow`):** @@ -504,40 +623,108 @@ This test creates a game, has both players play all 3 rounds with specific strat Reusable allocation strategies: -```rust -pub unconstrained fn balanced_allocation() -> (u8, u8, u8, u8, u8) { (2, 2, 2, 2, 1) } -pub unconstrained fn aggressive_allocation() -> (u8, u8, u8, u8, u8) { (3, 3, 3, 0, 0) } -pub unconstrained fn defensive_allocation() -> (u8, u8, u8, u8, u8) { (0, 0, 1, 4, 4) } +```rust title="allocation-strategies" showLineNumbers +// Common point allocations for testing +// Balanced strategy: distribute points evenly +pub unconstrained fn balanced_allocation() -> (u8, u8, u8, u8, u8) { + (2, 2, 2, 2, 1) +} + +// Aggressive strategy: focus on first 3 tracks +pub unconstrained fn aggressive_allocation() -> (u8, u8, u8, u8, u8) { + (3, 3, 3, 0, 0) +} + +// Defensive strategy: focus on last 2 tracks +pub unconstrained fn defensive_allocation() -> (u8, u8, u8, u8, u8) { + (0, 0, 1, 4, 4) +} + +// Maximum allowed points +pub unconstrained fn max_allocation() -> (u8, u8, u8, u8, u8) { + (5, 2, 1, 1, 0) +} ``` +Source code: /test/helpers.nr#Lallocation-strategies + And higher-level helpers: -```rust -pub unconstrained fn setup_two_player_game(env, contract_address, player1, player2, game_id) { ... } -pub unconstrained fn play_all_rounds_with_strategy(env, contract_address, player, game_id, allocations) { ... } +```rust title="setup-helpers" showLineNumbers +// Helper to setup a game with two players +pub unconstrained fn setup_two_player_game( + env: &mut TestEnvironment, + contract_address: AztecAddress, + player1: AztecAddress, + player2: AztecAddress, + game_id: Field +) { + env.call_public(player1, PodRacing::at(contract_address).create_game(game_id)); + env.call_public(player2, PodRacing::at(contract_address).join_game(game_id)); +} + +// Helper to play a round with specific allocations +pub unconstrained fn play_round_with_allocation( + env: &mut TestEnvironment, + contract_address: AztecAddress, + player: AztecAddress, + game_id: Field, + round: u8, + allocation: (u8, u8, u8, u8, u8) +) { + let (t1, t2, t3, t4, t5) = allocation; + env.call_private( + player, + PodRacing::at(contract_address).play_round(game_id, round, t1, t2, t3, t4, t5) + ); +} + +// Helper to play all 3 rounds with the same strategy +pub unconstrained fn play_all_rounds_with_strategy( + env: &mut TestEnvironment, + contract_address: AztecAddress, + player: AztecAddress, + game_id: Field, + allocations: [(u8, u8, u8, u8, u8); 3] +) { + for i in 0..3 { + let round = (i + 1) as u8; + play_round_with_allocation(env, contract_address, player, game_id, round, allocations[i]); + } +} ``` +Source code: /test/helpers.nr#Lsetup-helpers + #### Test setup (`src/test/utils.nr`) -```rust +```rust title="test-setup" showLineNumbers +// Setup function for pod racing contract tests +// Returns: (TestEnvironment, contract_address, admin_address) +// Note: Create player accounts in individual tests to avoid oracle errors pub unconstrained fn setup() -> (TestEnvironment, AztecAddress, AztecAddress) { let mut env = TestEnvironment::new(); + let admin = env.create_light_account(); + let initializer_call_interface = PodRacing::interface().constructor(admin); let contract_address = env.deploy("PodRacing").with_public_initializer(admin, initializer_call_interface); + (env, contract_address, admin) } ``` +Source code: /test/utils.nr#Ltest-setup + **Ethereum analogies:** -- `env.call_public(player, ...)` is like `vm.prank(player)` + calling a function in Foundry -- `env.call_private(player, ...)` is the same, but for private functions (no Foundry equivalent) -- `context.storage_read(slot)` is like `vm.load(address, slot)` in Foundry -- `env.create_light_account()` creates a test account +* `env.call_public(player, ...)` is like `vm.prank(player)` + calling a function in Foundry +* `env.call_private(player, ...)` is the same, but for private functions (no Foundry equivalent) +* `context.storage_read(slot)` is like `vm.load(address, slot)` in Foundry +* `env.create_light_account()` creates a test account ---- +*** ## Phase 3: Local Development Setup @@ -570,10 +757,10 @@ aztec start --local-network This starts: -- **Aztec node** — processes transactions, builds blocks -- **PXE** — Private eXecution Environment on `localhost:8080` -- **Anvil L1 chain** — local Ethereum L1 on `localhost:8545` -- **Protocol contracts** — deployed automatically on the L1 +* **Aztec node** — processes transactions, builds blocks +* **PXE** — Private eXecution Environment on `localhost:8080` +* **Anvil L1 chain** — local Ethereum L1 on `localhost:8545` +* **Protocol contracts** — deployed automatically on the L1 This is the Aztec equivalent of running `anvil` or `npx hardhat node`. @@ -624,11 +811,11 @@ this.configPath = path.resolve(process.cwd(), `config/${env}.json`); Key exports from `config/config.ts`: -- `getAztecNodeUrl()` — returns the node URL for the current environment -- `getTimeouts()` — returns environment-specific timeout values (local: 60s tx, devnet: 180s tx) -- `getEnv()` — returns the environment name (`"local-network"` or `"devnet"`) +* `getAztecNodeUrl()` — returns the node URL for the current environment +* `getTimeouts()` — returns environment-specific timeout values (local: 60s tx, devnet: 180s tx) +* `getEnv()` — returns the environment name (`"local-network"` or `"devnet"`) ---- +*** ## Phase 4: Deploy and Interact @@ -648,18 +835,16 @@ Every Aztec account is a smart contract. There are no Externally Owned Accounts The `SponsoredFPC` is a canonical Fee Payment Contract deployed at a deterministic address (salt = 0). It pays transaction fees on behalf of users, useful for onboarding when users don't have Fee Juice yet. On the local network it's pre-deployed. -```typescript -const SPONSORED_FPC_SALT = new Fr(0); +```typescript title="get-sponsored-fpc" showLineNumbers export async function getSponsoredFPCInstance(): Promise { - return await getContractInstanceFromInstantiationParams( - SponsoredFPCContract.artifact, - { - salt: SPONSORED_FPC_SALT, - }, - ); + return await getContractInstanceFromInstantiationParams(SponsoredFPCContractArtifact, { + salt: new Fr(SPONSORED_FPC_SALT), + }); } ``` +Source code: /utils/sponsored_fpc.ts#Lget-sponsored-fpc + **Run it:** ```bash @@ -747,12 +932,12 @@ The `beforeAll` block: Key tests: -- **Creates a game** — calls `create_game` and checks `TxStatus.SUCCESS` -- **Allows a second player to join** — sets up a game with both players -- **Plays a complete round** — private function call, verifies success -- **Rejects rounds with too many points** — expects the transaction to throw -- **Plays a full game from start to finish** — all rounds, both players, finish and reveal -- **Maintains privacy of round choices** — verifies round can be played without revealing allocations +* **Creates a game** — calls `create_game` and checks `TxStatus.SUCCESS` +* **Allows a second player to join** — sets up a game with both players +* **Plays a complete round** — private function call, verifies success +* **Rejects rounds with too many points** — expects the transaction to throw +* **Plays a full game from start to finish** — all rounds, both players, finish and reveal +* **Maintains privacy of round choices** — verifies round can be played without revealing allocations **Run them:** @@ -767,7 +952,7 @@ yarn test:js yarn test:nr ``` ---- +*** ## Phase 5: Write Your Own Code @@ -886,11 +1071,11 @@ yarn test:js ### 5.4 — Ideas for Further Modifications -- **Easy:** Change `TOTAL_ROUNDS` from 3 to 5, and the point budget from 9 to 15. Update constants and re-run tests. -- **Medium:** Add a `get_game_state` unconstrained view function that returns the public `Race` data for a given game ID. -- **Hard:** Add token wagers — import `TokenContract`, have players deposit tokens when joining, and transfer the pot to the winner on finalization. +* **Easy:** Change `TOTAL_ROUNDS` from 3 to 5, and the point budget from 9 to 15. Update constants and re-run tests. +* **Medium:** Add a `get_game_state` unconstrained view function that returns the public `Race` data for a given game ID. +* **Hard:** Add token wagers — import `TokenContract`, have players deposit tokens when joining, and transfer the pot to the winner on finalization. ---- +*** ## Phase 6: Advanced Topics @@ -980,7 +1165,7 @@ console.log(block?.header); yarn get-block ``` ---- +*** ## Appendix @@ -1053,8 +1238,8 @@ yarn get-block ### D. Links -- [Aztec Documentation](https://docs.aztec.network) -- [Aztec-nr (Noir framework for Aztec)](https://github.com/AztecProtocol/aztec-nr) -- [Noir Language](https://noir-lang.org) -- [Aztec Discord](https://discord.gg/aztec) -- [This repository](https://github.com/AztecProtocol/aztec-starter) +* [Aztec Documentation](https://docs.aztec.network) +* [Aztec-nr (Noir framework for Aztec)](https://github.com/AztecProtocol/aztec-nr) +* [Noir Language](https://noir-lang.org) +* [Aztec Discord](https://discord.gg/aztec) +* [This repository](https://github.com/AztecProtocol/aztec-starter) diff --git a/docs/ONBOARDING.src.md b/docs/ONBOARDING.src.md new file mode 100644 index 0000000..e5acad8 --- /dev/null +++ b/docs/ONBOARDING.src.md @@ -0,0 +1,857 @@ +# Onboarding: From Ethereum to Aztec + +This guide takes you from "reading code in a browser" to "deploying on devnet" — progressively, with no install required until Phase 3. + +**What you'll learn:** How Aztec contracts work by studying a Pod Racing game — a two-player competitive game that uses private state to implement commit-reveal in a single transaction. + +**How the guide is structured:** + +- **Phases 1-2** need only a browser (read code, compile in a Codespace) +- **Phases 3-6** need local tools (deploy, interact, extend, advanced topics) + +**Aztec version pinned in this repo:** `4.0.0-devnet.2-patch.1` (check `Nargo.toml` and `package.json` for source of truth) + +**Links:** + +- [Aztec Docs](https://docs.aztec.network) +- [Noir Language](https://noir-lang.org) +- [Discord](https://discord.gg/aztec) + +--- + +## Phase 1: Read and Understand (No Install Required) + +**Goal:** Build a mental model of Aztec by reading the contract code in your browser. + +### 1.1 — Aztec vs Ethereum: Quick Mental Model + +Aztec is an L2 on Ethereum with **native privacy**. Three core ideas separate it from a typical EVM rollup: + +1. **Two kinds of state.** Public state works like Ethereum storage — everyone can read it. Private state is encrypted; only the owner can read it. +2. **Noir** is the smart contract language. It looks like Rust and compiles to zero-knowledge circuits. +3. **PXE** (Private eXecution Environment) is a local client that manages your private state and builds proofs before sending transactions. There is no Ethereum equivalent. + +#### Ethereum-to-Aztec comparison table + +| Concept | Ethereum | Aztec | +| -------------------------------- | -------------------------- | --------------------------------------- | +| **Language** | Solidity | Noir | +| **Public state mapping** | `mapping(address => uint)` | `Map>` | +| **Private state model** | No equivalent | Notes, private encrypted state | +| **Caller context** | `msg.sender` | `context.msg_sender()` | +| **Public function declaration** | `public function` | `#[external("public")]` | +| **Private function declaration** | No native equivalent | `#[external("private")]` | +| **Commit-reveal pattern** | Manual commit-reveal | Built into the protocol | +| **Account model** | EOA accounts | Account abstraction | +| **Gas payment model** | ETH for gas | Fee Payment Contracts, sponsored fees | + +**Key concepts in more detail:** + +- **Notes** — Private state primitives. Think of them as encrypted UTXOs that only the owner can decrypt and read. When you "write" private state, you create a note. When you "read" it, your PXE decrypts it locally. ([Aztec docs: Notes](https://docs.aztec.network/developers/docs/foundational-topics/state_management#notes)) +- **Public vs Private functions** — Public functions execute on the network (like Solidity). Private functions execute locally in your PXE and produce a proof that gets verified on-chain. ([Aztec docs: Functions](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/functions)) +- **PXE** — Your local execution environment. It stores your private notes, builds proofs, and submits transactions. Each user runs their own PXE. ([Aztec docs: PXE](https://docs.aztec.network/aztec/concepts/pxe)) +- **Account abstraction** — Every Aztec account is a smart contract. There are no EOAs. This repo uses Schnorr signature accounts. ([Aztec docs: Accounts](https://docs.aztec.network/aztec/concepts/accounts)) + +### 1.2 — The Pod Racing Game: What It Does + +Think of managing a pod racing team: you have limited resources (crew, fuel, parts) and 5 race tracks to compete on over 3 rounds. Each round you secretly decide how to spread your resources across the tracks — go all-in on a few, or spread thin across all five? After all rounds, your totals are revealed and whoever dominated more tracks wins the series. The game isn't about speed — it's about strategy under hidden information, which is exactly what Aztec's private state enables. + +The Pod Racing contract ([`src/main.nr`](./src/main.nr)) is a two-player competitive game where players allocate points across 5 tracks over 3 rounds. It naturally requires commit-reveal (players shouldn't see each other's moves), making it a perfect Aztec demo. + +**Game flow:** + +1. Player 1 **creates** a game with a unique ID +2. Player 2 **joins** the game +3. Both players **play 3 rounds privately** — each round they distribute up to 9 points across 5 tracks +4. Both players **finish/reveal** — their private round notes are summed and the totals published +5. Anyone **finalizes** — the winner is determined by who won more tracks (best of 5) + +**Rules:** + +- 2 players per game +- 5 tracks, 3 rounds +- Each round: distribute up to 9 points across the 5 tracks +- After all rounds, each track's total is compared between players +- The player who wins 3+ tracks wins the game + +> Reference: top comment block in `src/main.nr` + +### 1.3 — Contract Walkthrough: Storage + +Open `src/main.nr` and look at the `Storage` struct: + +#include_code storage /main.nr rust + +**What is `Context`?** You'll notice `Context` appears as a generic parameter throughout the storage definition. In Aztec, the context is the execution environment passed to every function — it's how your contract accesses blockchain state like `context.msg_sender()` (the caller's address) and `context.block_number()`. Think of it as an expanded version of Solidity's global variables (`msg.sender`, `block.number`, etc.), but packaged as an object. The `` generic on storage types lets the same storage struct work in both public and private execution contexts. You don't need to construct it yourself — the framework provides `self.context` automatically in every contract function. + +What each field does: + +| Field | Type | Visibility | Purpose | +| ------------- | ---------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- | +| `admin` | `PublicMutable` | Public | Contract administrator address, set in constructor | +| `races` | `Map>` | Public | Maps `game_id` to a `Race` struct with player addresses, round progress, and final scores | +| `progress` | `Map>>` | **Private** | Maps `game_id` → `player` → set of private notes containing that player's round choices | +| `win_history` | `Map>` | Public | Career win count per player (leaderboard) | + +**Conceptual Solidity equivalent:** + +```solidity +// Solidity (approximate) +contract PodRacing { + address public admin; + mapping(uint256 => Race) public races; + // No Solidity equivalent for private state! + // Aztec's `progress` stores encrypted data only the owner can read + mapping(address => uint256) public winHistory; +} +``` + +**State variable types — one sentence each:** + +- [`PublicMutable`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#publicmutable) — A single public value that can be read and written by public functions. Like a Solidity state variable. +- [`Map`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#map) — A key-value mapping, like Solidity's `mapping`. +- [`PrivateSet`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#privateset) — A set of private notes. Notes can be inserted, read (by owner), and nullified. +- [`Owned`](https://docs.aztec.network/developers/docs/aztec-nr/framework-description/state_variables#owned) — A wrapper that scopes private state to a specific owner, so `progress.at(game_id).at(player)` returns only that player's notes. + +### 1.4 — Contract Walkthrough: Public Functions + +These functions should feel familiar if you've written Solidity. + +#### `constructor()` + +#include_code constructor /main.nr rust + +Sets the admin address. The `#[initializer]` macro means this runs once at deployment, like a Solidity constructor. + +#### `create_game()` + +#include_code create-game /main.nr rust + +Creates a new game. Checks the game ID isn't taken (player1 must be zero address), then writes a new `Race` struct with the caller as player1 and an expiration time. + +#### `join_game()` + +#include_code join-game /main.nr rust + +A second player joins. The `Race::join()` method validates that player1 exists, the player2 slot is empty, and the joiner isn't player1. + +#### `finalize_game()` + +#include_code finalize-game /main.nr rust + +After both players have revealed, this compares track scores, determines the winner, and updates the leaderboard. + +#### The `Race` struct ([`src/race.nr`](./src/race.nr)) + +The `Race` struct stores all public game state. It has 17 fields: + +#include_code race-struct /race.nr rust + +Key methods: + +- **`Race::new()`** — Creates a game with player1, zero'd scores, and an expiration block +- **`Race::join()`** — Adds player2 after validating the game is joinable and the player isn't playing themselves +- **`Race::calculate_winner()`** — Compares each track's totals, counts wins per player, returns the address of whoever won 3+ tracks (ties go to player2) + +### 1.5 — Contract Walkthrough: Private Functions (The Key Difference) + +This is the "aha moment" — the part with no Ethereum equivalent. + +#### `play_round()` + +#include_code play-round /main.nr rust + +Three things happen here that have no direct Ethereum equivalent: + +1. **Point constraint** — `track1 + track2 + ... < 10` is enforced in the ZK circuit. The prover can't cheat. +2. **Creating a private note** — `.insert(GameRoundNote::new(...))` stores the round choices as an encrypted note. Only the player can read it later. The `.deliver(MessageDelivery.CONSTRAINED_ONCHAIN)` commits the note's hash on-chain without revealing the content. +3. **Enqueuing a public call** — `self.enqueue(...)` schedules a public function to run after the private proof is verified. This updates the round counter publicly (so both players can see progress) without revealing the point allocation. + +#### `finish_game()` + +#include_code finish-game /main.nr rust + +This is the "reveal" phase: + +- **Reading own private notes** — `get_notes(NoteGetterOptions::new())` retrieves the player's encrypted round notes. Only the owner's PXE can decrypt these. +- **Summing and publishing** — The totals are calculated privately, then the enqueued public call writes them on-chain for everyone to see. + +#### `GameRoundNote` (`src/game_round_note.nr`) + +#include_code game-round-note /game_round_note.nr rust + +The `#[note]` macro makes this a private state primitive. Each note stores one round's point allocation and the owner's address. Only the owner can read it. + +#### Internal functions: `#[only_self]` + +Two functions are marked `#[only_self]`, meaning they can only be called by the contract itself (via `self.enqueue(...)`): + +- **`validate_and_play_round`** — Validates the round is sequential and increments the player's round counter +- **`validate_finish_game_and_reveal`** — Stores the player's revealed track totals, checking they haven't already been revealed + +**Key insight:** On Ethereum, commit-reveal requires at least 2 transactions (one to commit, one to reveal after a delay). On Aztec, the "commit" happens automatically when a private function creates a note — the data is committed on-chain (as a hash) without ever being visible. The "reveal" is a separate transaction, but the privacy was enforced by the protocol the whole time. + +But the deeper point isn't just "commit-reveal is easier." The real benefit of Aztec is **composability in private execution**. In `play_round`, a private function validates a constraint (`track1 + ... < 10`), stores an encrypted note, and enqueues a public state update — all in one atomic transaction. On a public blockchain, you simply cannot compose contract logic over hidden inputs like this. Any "private" scheme on Ethereum (e.g. commit-reveal, ZK proofs submitted to a verifier contract) requires the application developer to build all the privacy infrastructure themselves, and each private component is an isolated island — not just within one app, but across apps too. A private game on Ethereum cannot automatically privately compose with a private token or a private identity contract, because each one rolls its own incompatible privacy scheme. On Aztec, private functions call other private functions, read private state, and interact with public state through a unified execution model — privacy is a first-class property of the entire contract system, not a bolt-on per application. A private game can call a private token's `transfer` in the same transaction, and both sides stay private. + +```mermaid +graph TD + subgraph Ethereum["Ethereum: Privacy is DIY"] + direction TB + E_Game["Private Game
custom commit-reveal"] + E_Token["Private Token
custom ZK proofs"] + E_Identity["Private Identity
custom encryption"] + E_Game -.-|"cannot compose"| E_Token + E_Token -.-|"cannot compose"| E_Identity + E_Game -.-|"cannot compose"| E_Identity + + style E_Game fill:#fee,stroke:#c33 + style E_Token fill:#fee,stroke:#c33 + style E_Identity fill:#fee,stroke:#c33 + end + + subgraph Aztec["Aztec: Privacy is Built In"] + direction TB + A_Game["Private Game
play_round"] + A_Token["Private Token
transfer"] + A_Identity["Private Identity
verify"] + A_Game -->|"private call"| A_Token + A_Token -->|"private call"| A_Identity + A_Game -->|"private call"| A_Identity + + style A_Game fill:#efe,stroke:#3a3 + style A_Token fill:#efe,stroke:#3a3 + style A_Identity fill:#efe,stroke:#3a3 + end + + Ethereum ~~~ Aztec +``` + +> **Reading the diagram:** On Ethereum (left), each private application builds its own incompatible privacy infrastructure — they cannot compose with each other. A private game can't privately call a private token. On Aztec (right), any private contract can call any other private contract in a single atomic transaction. Privacy composes across the entire ecosystem, not just within one app. + +### 1.6 — Game Flow: What's Private, What's Public + +Here's exactly what an outside observer can and cannot see at each step. + +Some functions are labeled **"private, then public"** in the Type column. On Aztec, there are only two function types: `#[private]` and `#[public]`. But a private function can _enqueue_ a public function to run after it — within the same transaction. The private part runs first (on the user's machine, hidden from everyone), then the public part runs on-chain (visible to all). This is how the contract hides sensitive data while still updating shared public state. + +| Step | Function | Type | Observer **CAN** see | Observer **CANNOT** see | +| ---- | --------------- | ------------------- | --------------------------------------------------------- | ---------------------------------------------- | +| 1 | `create_game` | public | Game created, player1 address, expiration block | Nothing hidden | +| 2 | `join_game` | public | Player2 joined, both addresses | Nothing hidden | +| 3 | `play_round` | private, then public | Round counter incremented (e.g. "player1 played round 1") | Point allocation across tracks | +| 4 | `finish_game` | private, then public | Final track totals revealed (e.g. "player1: 7,7,7,3,3") | Individual round allocations | +| 5 | `finalize_game` | public | Winner declared, leaderboard updated | Nothing hidden (all data public at this point) | + +The critical privacy window is between steps 3 and 4: both players have committed their strategies (as private notes), but neither can see the other's choices. This prevents the second player from gaining an advantage by observing the first player's moves. + +--- + +## Phase 2: Compile and Test in the Cloud (Zero Install) + +:warning: **Important:** This phase uses GitHub Codespaces to provide a cloud-based dev environment with all dependencies pre-installed. This can be slow to run on a basic codespace instance, you may want to jump directly to running this on your local machine in [Phase 3](#phase-3-local-development-setup). + +**Goal:** Get hands-on with compilation and Noir tests using only a GitHub Codespace. + +### 2.1 — Launch a GitHub Codespace + +1. Go to the [aztec-starter repo on GitHub](https://github.com/AztecProtocol/aztec-starter) +2. Click the green **Code** button, then **Codespaces** tab, then **Create codespace on main** +3. Wait for the Codespace to build and the `postCreateCommand` to finish + +The `.devcontainer/` configures: + +- **Base image:** Ubuntu 24.04 with Node.js v22.15.0 +- **Docker-in-Docker** for running the Aztec local network +- **Aztec CLI** installed via `curl -fsSL "https://install.aztec.network/4.0.0-devnet.2-patch.1" | VERSION="4.0.0-devnet.2-patch.1" bash -s` +- **VS Code extension:** `noir-lang.vscode-noir` for Noir syntax highlighting +- **Dependencies:** `yarn install` runs automatically + +### 2.2 — Compile the Contract + +```bash +yarn compile +``` + +This runs `aztec compile`, which compiles the Noir contract in `src/main.nr` to artifacts in `./target/`. This is like running `forge build` in Foundry. + +Then generate TypeScript bindings: + +```bash +yarn codegen +``` + +This runs `aztec codegen target --outdir src/artifacts` and generates `./src/artifacts/PodRacing.ts` — a TypeScript wrapper class (like TypeChain for Solidity). The generated `PodRacingContract` class gives you: + +- `PodRacingContract.deploy(wallet, admin)` — deploy a new instance +- `PodRacingContract.at(address, wallet)` — connect to an existing instance +- `contract.methods.create_game(gameId)` — call any contract function + +> Reference: `Nargo.toml` is the project manifest (like `foundry.toml`). It specifies the package name, type (`contract`), and the `aztec-nr` dependency version. + +### 2.3 — Run Noir Unit Tests (No Network Required) + +```bash +yarn test:nr +``` + +This runs `aztec test`, which uses Aztec's **TXE** (Testing eXecution Environment) — a lightweight test runtime similar to Foundry's `forge test`. No network or Docker required. + +#### Test structure + +Tests live in `src/test/`: + +- `mod.nr` — declares the test modules +- `utils.nr` — test setup (deploy contract, create admin) +- `helpers.nr` — reusable helpers (strategies, game setup, round playing) +- `pod_racing.nr` — the actual test cases + +#### Key test patterns in [`src/test/pod_racing.nr`](./src/test/pod_racing.nr) + +**Basic initialization test:** + +#include_code test-initializer /test/pod_racing.nr rust + +The `unconstrained` keyword means this test runs outside the ZK circuit (it's a test, not a provable function). `utils::setup()` deploys a fresh contract and returns the environment, contract address, and admin. + +**Expected failure test:** + +#include_code test-fail-too-many-points /test/pod_racing.nr rust + +The `#[test(should_fail)]` attribute is like Foundry's `vm.expectRevert()`. + +**Full game flow test (`test_full_game_flow`):** + +This test creates a game, has both players play all 3 rounds with specific strategies, calls `finish_game` for both, and verifies the exact stored scores. It uses helper functions from `helpers.nr`. + +#### Test helpers (`src/test/helpers.nr`) + +Reusable allocation strategies: + +#include_code allocation-strategies /test/helpers.nr rust + +And higher-level helpers: + +#include_code setup-helpers /test/helpers.nr rust + +#### Test setup (`src/test/utils.nr`) + +#include_code test-setup /test/utils.nr rust + +**Ethereum analogies:** + +- `env.call_public(player, ...)` is like `vm.prank(player)` + calling a function in Foundry +- `env.call_private(player, ...)` is the same, but for private functions (no Foundry equivalent) +- `context.storage_read(slot)` is like `vm.load(address, slot)` in Foundry +- `env.create_light_account()` creates a test account + +--- + +## Phase 3: Local Development Setup + +**Goal:** Set up a full local Aztec development environment. + +### 3.1 — Install Prerequisites + +**Node.js v22.15.0** — use [nvm](https://github.com/nvm-sh/nvm) or your preferred version manager. + +**Aztec toolkit:** + +```bash +export VERSION=4.0.0-devnet.2-patch.1 +curl -fsSL "https://install.aztec.network/${VERSION}" | VERSION="${VERSION}" bash -s +``` + +**Project dependencies:** + +```bash +yarn install +``` + +### 3.2 — Start the Local Network + +In a separate terminal: + +```bash +aztec start --local-network +``` + +This starts: + +- **Aztec node** — processes transactions, builds blocks +- **PXE** — Private eXecution Environment on `localhost:8080` +- **Anvil L1 chain** — local Ethereum L1 on `localhost:8545` +- **Protocol contracts** — deployed automatically on the L1 + +This is the Aztec equivalent of running `anvil` or `npx hardhat node`. + +> **Warning:** If you restart the local network, delete the `./store` directory to avoid stale PXE state: +> +> ```bash +> rm -rf ./store +> ``` + +### 3.3 — Understand the Config System + +The project uses JSON config files selected by the `AZTEC_ENV` environment variable. + +**`config/config.ts`** — A `ConfigManager` singleton that loads the appropriate JSON file: + +```typescript +const env = process.env.AZTEC_ENV || "local-network"; +this.configPath = path.resolve(process.cwd(), `config/${env}.json`); +``` + +**`config/local-network.json`:** + +```json +{ + "name": "local-network", + "environment": "local", + "network": { + "nodeUrl": "http://localhost:8080", + "l1RpcUrl": "http://localhost:8545", + "l1ChainId": 31337 + } +} +``` + +**`config/devnet.json`:** + +```json +{ + "name": "devnet", + "environment": "devnet", + "network": { + "nodeUrl": "https://next.devnet.aztec-labs.com", + "l1RpcUrl": "https://ethereum-sepolia-rpc.publicnode.com", + "l1ChainId": 11155111 + } +} +``` + +Key exports from `config/config.ts`: + +- `getAztecNodeUrl()` — returns the node URL for the current environment +- `getTimeouts()` — returns environment-specific timeout values (local: 60s tx, devnet: 180s tx) +- `getEnv()` — returns the environment name (`"local-network"` or `"devnet"`) + +--- + +## Phase 4: Deploy and Interact + +**Goal:** Deploy accounts and contracts, interact with the Pod Racing game. + +### 4.1 — Deploy an Account + +Every Aztec account is a smart contract. There are no Externally Owned Accounts (EOAs). You must deploy your account before you can send transactions. + +**How it works ([`src/utils/deploy_account.ts`](src/utils/deploy_account.ts)):** + +1. Generate keys: `Fr.random()` for the secret key and salt, `GrumpkinScalar.random()` for the signing key +2. Create a Schnorr account: `wallet.createSchnorrAccount(secretKey, salt, signingKey)` +3. Deploy it using sponsored fees: `account.getDeployMethod().send({ fee: { paymentMethod: sponsoredPaymentMethod } })` + +**Sponsored fees (`src/utils/sponsored_fpc.ts`):** + +The `SponsoredFPC` is a canonical Fee Payment Contract deployed at a deterministic address (salt = 0). It pays transaction fees on behalf of users, useful for onboarding when users don't have Fee Juice yet. On the local network it's pre-deployed. + +#include_code get-sponsored-fpc /utils/sponsored_fpc.ts typescript + +**Run it:** + +```bash +yarn deploy-account +``` + +Save the output (secret key, signing key, salt) to a `.env` file. See `.env.example` for the format: + +```bash +SECRET="0x..." +SIGNING_KEY="0x..." +SALT="0x..." +AZTEC_ENV=local-network +``` + +### 4.2 — Deploy the Contract + +**How it works (`scripts/deploy_contract.ts`):** + +1. Set up wallet via `setupWallet()` (connects to the node, creates a TestWallet) +2. Register the `SponsoredFPC` for fee payment +3. Deploy a Schnorr account (or use one from env) +4. Deploy the contract: + +```typescript +const deployRequest = PodRacingContract.deploy(wallet, address); +await deployRequest.simulate({ from: address }); +const { contract: podRacingContract, instance } = await deployRequest.send({ + from: address, + fee: { paymentMethod: sponsoredPaymentMethod }, + wait: { timeout: timeouts.deployTimeout, returnReceipt: true } +}); +``` + +> **Important:** Always call `.simulate()` before `.send()`. Simulation runs the transaction locally and surfaces revert reasons immediately. Without it, a failing transaction hangs until timeout with an opaque error. + +**Run it:** + +```bash +yarn deploy +``` + +The output includes the contract address, admin address, and instantiation data (salt, deployer, constructor args). Save these for interacting with the contract later. + +### 4.3 — Interact with a Deployed Contract + +**How it works (`scripts/interaction_existing_contract.ts`):** + +1. Load your account from env: `getAccountFromEnv(wallet)` reads `SECRET`, `SIGNING_KEY`, and `SALT` from `.env` +2. Reconstruct the contract instance from env vars (`CONTRACT_SALT`, `CONTRACT_DEPLOYER`, `CONTRACT_CONSTRUCTOR_ARGS`) +3. Register the contract with the wallet: `wallet.registerContract(instance, PodRacingContract.artifact)` +4. Call methods: + +```typescript +const podRacingContract = await PodRacingContract.at(contractAddress, wallet); + +// Simulate first — surfaces revert reasons instantly +await podRacingContract.methods.create_game(gameId).simulate({ from: address }); + +// Then send — only after simulation succeeds +await podRacingContract.methods.create_game(gameId) + .send({ + from: address, + fee: { paymentMethod: sponsoredPaymentMethod }, + wait: { timeout: timeouts.txTimeout } + }); +``` + +Set the env vars from your deploy output, then run: + +```bash +yarn interaction-existing-contract +``` + +### 4.4 — Run E2E Tests + +**How it works (`src/test/e2e/index.test.ts`):** + +The `beforeAll` block: + +1. Sets up a `TestWallet` via `setupWallet()` +2. Registers the `SponsoredFPC` +3. Creates two Schnorr player accounts with random keys +4. Deploys the Pod Racing contract with player1 as admin + +Key tests: + +- **Creates a game** — calls `create_game` and checks `TxStatus.SUCCESS` +- **Allows a second player to join** — sets up a game with both players +- **Plays a complete round** — private function call, verifies success +- **Rejects rounds with too many points** — expects the transaction to throw +- **Plays a full game from start to finish** — all rounds, both players, finish and reveal +- **Maintains privacy of round choices** — verifies round can be played without revealing allocations + +**Run them:** + +```bash +# Both Noir unit tests and TypeScript E2E tests +yarn test + +# Just TypeScript E2E tests (requires local network running) +yarn test:js + +# Just Noir unit tests (no network required) +yarn test:nr +``` + +--- + +## Phase 5: Write Your Own Code + +**Goal:** Modify the contract and write tests. + +### 5.1 — Guided Exercise: Add a Forfeit Function + +Add a `forfeit_game` function to `src/main.nr` that lets a player concede: + +```rust +#[external("public")] +fn forfeit_game(game_id: Field) { + let game = self.storage.races.at(game_id).read(); + let caller = self.context.maybe_msg_sender().unwrap(); + + // Only a player in this game can forfeit + assert(caller.eq(game.player1) | caller.eq(game.player2)); + + // The other player wins + let winner = if caller.eq(game.player1) { + game.player2 + } else { + game.player1 + }; + + // Update win history + let previous_wins = self.storage.win_history.at(winner).read(); + self.storage.win_history.at(winner).write(previous_wins + 1); +} +``` + +Compile and regenerate TypeScript bindings: + +```bash +yarn compile && yarn codegen +``` + +### 5.2 — Write a Noir Test + +Add a test to `src/test/pod_racing.nr`: + +```rust +#[test] +unconstrained fn test_forfeit_game() { + let (mut env, contract_address, _) = utils::setup(); + let player1 = env.create_light_account(); + let player2 = env.create_light_account(); + let game_id = helpers::TEST_GAME_ID_9; + + // Setup game + helpers::setup_two_player_game(&mut env, contract_address, player1, player2, game_id); + + // Player 1 forfeits + env.call_public(player1, PodRacing::at(contract_address).forfeit_game(game_id)); + + // Verify player2's win count increased + env.public_context_at(contract_address, |context| { + let win_slot = derive_storage_slot_in_map( + PodRacing::storage_layout().win_history.slot, + player2.to_field() + ); + let wins = context.storage_read(win_slot); + assert_eq(wins, 1); + }); +} +``` + +Run: + +```bash +yarn test:nr +``` + +### 5.3 — Write a TypeScript E2E Test + +Add a test case to `src/test/e2e/index.test.ts`: + +```typescript +it("Allows a player to forfeit", async () => { + const gameId = new Fr(300); + + await setupGame( + contract, + gameId, + player1Account.address, + player2Account.address, + sponsoredPaymentMethod, + getTimeouts().txTimeout, + ); + + // Simulate first to surface revert reasons before sending + await contract.methods.forfeit_game(gameId).simulate({ + from: player1Account.address, + }); + + const tx = await contract.methods.forfeit_game(gameId).send({ + from: player1Account.address, + fee: { paymentMethod: sponsoredPaymentMethod }, + wait: { timeout: getTimeouts().txTimeout }, + }); + + expect([ + TxStatus.PROPOSED, + TxStatus.CHECKPOINTED, + TxStatus.PROVEN, + TxStatus.FINALIZED, + ]).toContain(tx.status); +}, 600000); +``` + +Run: + +```bash +yarn test:js +``` + +### 5.4 — Ideas for Further Modifications + +- **Easy:** Change `TOTAL_ROUNDS` from 3 to 5, and the point budget from 9 to 15. Update constants and re-run tests. +- **Medium:** Add a `get_game_state` unconstrained view function that returns the public `Race` data for a given game ID. +- **Hard:** Add token wagers — import `TokenContract`, have players deposit tokens when joining, and transfer the pot to the winner on finalization. + +--- + +## Phase 6: Advanced Topics + +**Goal:** Explore multi-wallet patterns, fee strategies, devnet, and profiling. + +### 6.1 — Multiple Wallets / Multiple PXEs + +**Why this matters:** In a real application, each player runs their own PXE. Player 1's PXE holds Player 1's private notes. Player 2's PXE holds Player 2's. + +**How it works (`scripts/multiple_wallet.ts`):** + +The script creates two independent PXE instances (wallet1, wallet2), each with their own key store: + +```typescript +const store1 = await createStore('pxe1', { dataDirectory: 'store', ... }); +const store2 = await createStore('pxe2', { dataDirectory: 'store', ... }); +const wallet1 = await TestWallet.create(node, fullConfig, { store: store1 }); +const wallet2 = await TestWallet.create(node, fullConfig, { store: store2 }); +``` + +It then deploys a Token contract from wallet1, creates an account on wallet2, mints tokens to wallet2's account, registers the token contract on wallet2, and reads balances. + +Key concept: `wallet2.registerContract(...)` is necessary because wallet2's PXE doesn't automatically know about contracts deployed by wallet1. Each PXE maintains its own registry. + +```bash +yarn multiple-wallet +``` + +### 6.2 — Fee Payment Strategies + +**`scripts/fees.ts`** demonstrates all four fee payment methods: + +1. **Sponsored** (`SponsoredFeePaymentMethod`) — A pre-deployed SponsoredFPC contract pays your fees. Simplest option, used throughout this repo. + +2. **Fee Juice** (`FeeJuicePaymentMethodWithClaim`) — Bridge ETH from L1 to get Fee Juice (native L2 gas token), then use it to pay fees directly. Requires L1 interaction. + +3. **Private fees** (`PrivateFeePaymentMethod`) — Pay fees through a Fee Payment Contract (FPC) using a private token transfer. The fee payment itself is private. + +4. **Public fees** (`PublicFeePaymentMethod`) — Same FPC mechanism but the token transfer is public. + +```bash +yarn fees +``` + +### 6.3 — Deploying to Devnet + +All scripts support a `::devnet` suffix: + +```bash +yarn deploy::devnet +yarn deploy-account::devnet +yarn interaction-existing-contract::devnet +yarn test::devnet +``` + +Devnet uses real provers and connects to the Aztec devnet at `https://next.devnet.aztec-labs.com` with Sepolia as the L1. Timeouts are longer (deploy: 20 min, tx: 3 min) to account for real proving time. + +### 6.4 — Transaction Profiling + +**`scripts/profile_deploy.ts`** shows how to profile a transaction: + +```typescript +const profileTx = await PodRacingContract.deploy(wallet, address).profile({ + profileMode: "full", + from: address, +}); +console.dir(profileTx, { depth: 2 }); +``` + +The `.profile()` method runs the transaction through the prover and returns detailed metrics about gate counts and proving time. + +```bash +yarn profile +``` + +### 6.5 — Querying Blocks + +**`scripts/get_block.ts`** shows how to query the Aztec node directly: + +```typescript +const node = createAztecNodeClient(nodeUrl); +let block = await node.getBlock(BlockNumber(1)); +console.log(block?.header); +``` + +```bash +yarn get-block +``` + +--- + +## Appendix + +### A. File Map + +| File | Purpose | +| ------------------------------------------ | ------------------------------------------------------ | +| `src/main.nr` | Pod Racing contract — all public and private functions | +| `src/race.nr` | `Race` struct — public game state and logic | +| `src/game_round_note.nr` | `GameRoundNote` — private note for round choices | +| `src/test/mod.nr` | Declares test modules | +| `src/test/pod_racing.nr` | Noir unit tests for the contract | +| `src/test/helpers.nr` | Test helpers — strategies, game setup | +| `src/test/utils.nr` | Test setup — deploy contract, create admin | +| `src/test/e2e/index.test.ts` | TypeScript E2E tests (Jest) | +| `src/test/e2e/public_logging.test.ts` | Logging E2E tests | +| `src/artifacts/PodRacing.ts` | Generated TypeScript contract bindings | +| `src/utils/deploy_account.ts` | Deploy a Schnorr account | +| `src/utils/sponsored_fpc.ts` | SponsoredFPC instance helper | +| `src/utils/setup_wallet.ts` | Wallet setup (connects to node) | +| `src/utils/create_account_from_env.ts` | Load account from `.env` vars | +| `scripts/deploy_contract.ts` | Deploy the Pod Racing contract | +| `scripts/deploy_account.ts` | Deploy account entry point | +| `scripts/interaction_existing_contract.ts` | Interact with a deployed contract | +| `scripts/multiple_wallet.ts` | Multi-PXE / multi-wallet demo | +| `scripts/fees.ts` | Fee payment strategies demo | +| `scripts/profile_deploy.ts` | Transaction profiling | +| `scripts/read_debug_logs.ts` | Debug logging utility demo | +| `scripts/get_block.ts` | Block querying | +| `config/config.ts` | Config manager (loads JSON by env) | +| `config/local-network.json` | Local network configuration | +| `config/devnet.json` | Devnet configuration | +| `Nargo.toml` | Noir project manifest | +| `.devcontainer/devcontainer.json` | GitHub Codespace configuration | +| `.devcontainer/Dockerfile` | Codespace Docker image | +| `.env.example` | Example environment variables | + +### B. Common Commands + +| Command | Description | +| ------------------------------------ | -------------------------------------------------- | +| `yarn compile` | Compile Noir contract to `./target/` | +| `yarn codegen` | Generate TypeScript bindings to `./src/artifacts/` | +| `yarn test` | Run both Noir and TypeScript tests | +| `yarn test:nr` | Run Noir unit tests only (no network) | +| `yarn test:js` | Run TypeScript E2E tests (needs local network) | +| `yarn deploy` | Deploy account + contract to local network | +| `yarn deploy-account` | Deploy a Schnorr account | +| `yarn interaction-existing-contract` | Interact with a deployed contract | +| `yarn multiple-wallet` | Multi-PXE demo | +| `yarn fees` | Fee payment methods demo | +| `yarn profile` | Profile a transaction | +| `yarn read-logs` | Demo debug logging utility | +| `yarn get-block` | Query block data | +| `yarn clean` | Delete `./src/artifacts` and `./target` | +| `yarn clear-store` | Delete `./store` (PXE data) | +| `yarn deploy::devnet` | Deploy to devnet | +| `yarn test::devnet` | Run E2E tests on devnet | + +### C. Troubleshooting + +| Problem | Solution | +| -------------------------------------------------------- | ------------------------------------------------------------------------ | +| "Store" or PXE errors after restarting the local network | Delete `./store`: `rm -rf ./store` | +| Compilation errors after updating dependencies | Run `yarn compile` again | +| Timeout errors on devnet | Check timeout values in `config/devnet.json` (deploy: 20 min, tx: 3 min) | +| "Contract not registered" error | Call `wallet.registerContract(instance, artifact)` before interacting | +| Account not found | Ensure `.env` has correct `SECRET`, `SIGNING_KEY`, and `SALT` values | +| Local network not starting | Ensure Docker is running and the correct Aztec version is installed | + +### D. Links + +- [Aztec Documentation](https://docs.aztec.network) +- [Aztec-nr (Noir framework for Aztec)](https://github.com/AztecProtocol/aztec-nr) +- [Noir Language](https://noir-lang.org) +- [Aztec Discord](https://discord.gg/aztec) +- [This repository](https://github.com/AztecProtocol/aztec-starter) diff --git a/package.json b/package.json index b69493f..a3889f4 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "test::devnet": "AZTEC_ENV=devnet yarn test:js", "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", "test:nr": "aztec test", + "docs:build": "remark docs/ONBOARDING.src.md -o ONBOARDING.md", "update-readme-version": "node ./.github/scripts/update-readme-version.js" }, "dependencies": { @@ -51,7 +52,10 @@ "@types/jest": "^29.5.11", "@types/mocha": "^10.0.6", "@types/node": "^22.15.1", + "include_code": "^0.1.0", "jest": "^29.7.0", + "remark": "^15.0.1", + "remark-cli": "^12.0.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.2", "typescript": "^5.4.4" diff --git a/src/game_round_note.nr b/src/game_round_note.nr index 008e5ae..aac069b 100644 --- a/src/game_round_note.nr +++ b/src/game_round_note.nr @@ -1,5 +1,6 @@ use aztec::{macros::notes::note, protocol::{traits::Packable, address::AztecAddress}}; +// docs:start:game-round-note // GameRoundNote is a private note that stores a player's point allocation for one round // These notes remain private until the player calls finish_game to reveal their totals // @@ -25,8 +26,10 @@ pub struct GameRoundNote { // The player who created this note (only they can read it) pub owner: AztecAddress, } +// docs:end:game-round-note impl GameRoundNote { + // docs:start:game-round-note-new // Creates a new note with the player's round choices // This note gets stored privately and can only be read by the owner pub fn new(track1: u8, track2: u8, track3: u8, track4: u8, track5: u8, round: u8, owner: AztecAddress) -> Self { @@ -40,6 +43,7 @@ impl GameRoundNote { owner, } } + // docs:end:game-round-note-new // Helper method to access the note data pub fn get(self) -> Self { diff --git a/src/main.nr b/src/main.nr index a8d74b8..9199e9a 100644 --- a/src/main.nr +++ b/src/main.nr @@ -37,6 +37,7 @@ pub contract PodRacing { global TOTAL_ROUNDS: u8 = 3; // Each game consists of 3 rounds global GAME_LENGTH: u32 = 300; // Games expire after 300 blocks + // docs:start:storage #[storage] struct Storage { // Contract administrator address @@ -55,14 +56,18 @@ pub contract PodRacing { // Public leaderboard tracking career victories win_history: Map, Context>, } + // docs:end:storage + // docs:start:constructor #[external("public")] #[initializer] fn constructor(admin: AztecAddress) { debug_log_format("Initializing PodRacing contract with admin {0}", [admin.to_field()]); self.storage.admin.write(admin); } + // docs:end:constructor + // docs:start:create-game // Creates a new game instance // The caller becomes player1 and waits for an opponent to join // Sets the game expiration to current block + GAME_LENGTH @@ -85,7 +90,9 @@ pub contract PodRacing { ); self.storage.races.at(game_id).write(game); } + // docs:end:create-game + // docs:start:join-game // Allows a second player to join an existing game // After joining, both players can start playing rounds #[external("public")] @@ -99,7 +106,9 @@ pub contract PodRacing { let joined_game = maybe_existing_game.join(player2); self.storage.races.at(game_id).write(joined_game); } + // docs:end:join-game + // docs:start:play-round // Plays a single round by allocating points across 5 tracks // This is a PRIVATE function - the point allocation remains hidden from the opponent // Players must play rounds sequentially (round 1, then 2, then 3) @@ -148,7 +157,9 @@ pub contract PodRacing { round, )); } + // docs:end:play-round + // docs:start:validate-and-play-round // Internal public function to validate and record that a player completed a round // Updates the public game state to track which round each player is on // Does NOT reveal the point allocation (that remains private) @@ -163,7 +174,9 @@ pub contract PodRacing { // Increment the player's round counter (validates sequential play) self.storage.races.at(game_id).write(game_in_progress.increment_player_round(player, round)); } + // docs:end:validate-and-play-round + // docs:start:finish-game // Called after all rounds are complete to reveal a player's total scores // This is PRIVATE - only the caller can read their own GameRoundNotes // The function sums up all round allocations per track and publishes totals @@ -214,7 +227,9 @@ pub contract PodRacing { total_track5, )); } + // docs:end:finish-game + // docs:start:validate-finish-game // Internal public function to store a player's revealed track totals // Validates that the player hasn't already revealed their scores (all must be 0) // After both players call finish_game, all scores are public and can be compared @@ -249,7 +264,9 @@ pub contract PodRacing { total_track5, )); } + // docs:end:validate-finish-game + // docs:start:finalize-game // Determines the winner after both players have revealed their scores // Can only be called after the game's end_block (time limit expired) // Compares track totals and declares the player who won more tracks as winner @@ -276,6 +293,7 @@ pub contract PodRacing { ); self.storage.win_history.at(winner).write(previous_wins + 1); } + // docs:end:finalize-game // Utility function: runs client-side in the PXE, not on-chain. // Reads the public game state and logs it via debug_log_format. diff --git a/src/race.nr b/src/race.nr index ec0370b..1c30b5f 100644 --- a/src/race.nr +++ b/src/race.nr @@ -4,6 +4,7 @@ use ::aztec::protocol::{ traits::{Deserialize, Serialize, Packable, ToField}, }; +// docs:start:race-struct // Race struct stores the public state of a game // This data is visible to everyone and tracks game progress #[derive(Deserialize, Serialize, Eq, Packable)] @@ -37,6 +38,7 @@ pub struct Race { // Block number when the game expires (for timeout enforcement) pub end_block: u32, } +// docs:end:race-struct impl Race { // Creates a new game with player1 set, waiting for player2 to join @@ -250,6 +252,7 @@ impl Race { debug_log_format("End block: {0}", [self.end_block as Field]); } + // docs:start:calculate-winner // Determines the game winner by comparing track scores // Winner is whoever won more tracks (best of 5) // @@ -305,4 +308,5 @@ impl Race { self.player2 } } + // docs:end:calculate-winner } diff --git a/src/test/helpers.nr b/src/test/helpers.nr index f915f60..af73dbe 100644 --- a/src/test/helpers.nr +++ b/src/test/helpers.nr @@ -15,6 +15,7 @@ pub global TEST_GAME_ID_8: Field = 8; pub global TEST_GAME_ID_9: Field = 9; pub global TEST_GAME_ID_10: Field = 10; +// docs:start:allocation-strategies // Common point allocations for testing // Balanced strategy: distribute points evenly pub unconstrained fn balanced_allocation() -> (u8, u8, u8, u8, u8) { @@ -35,7 +36,9 @@ pub unconstrained fn defensive_allocation() -> (u8, u8, u8, u8, u8) { pub unconstrained fn max_allocation() -> (u8, u8, u8, u8, u8) { (5, 2, 1, 1, 0) } +// docs:end:allocation-strategies +// docs:start:setup-helpers // Helper to setup a game with two players pub unconstrained fn setup_two_player_game( env: &mut TestEnvironment, @@ -77,3 +80,4 @@ pub unconstrained fn play_all_rounds_with_strategy( play_round_with_allocation(env, contract_address, player, game_id, round, allocations[i]); } } +// docs:end:setup-helpers diff --git a/src/test/pod_racing.nr b/src/test/pod_racing.nr index 1909c27..1e57104 100644 --- a/src/test/pod_racing.nr +++ b/src/test/pod_racing.nr @@ -6,6 +6,7 @@ use crate::PodRacing; // INITIALIZATION TESTS // ============================================================================ +// docs:start:test-initializer // Test: Contract initialization sets admin correctly #[test] unconstrained fn test_initializer() { @@ -16,6 +17,7 @@ unconstrained fn test_initializer() { assert_eq(current_admin, admin); }); } +// docs:end:test-initializer // ============================================================================ // GAME CREATION TESTS @@ -135,6 +137,7 @@ unconstrained fn test_play_round() { }); } +// docs:start:test-fail-too-many-points // Test: Cannot allocate more than 9 points in a round #[test(should_fail)] unconstrained fn test_fail_play_round_too_many_points() { @@ -151,6 +154,7 @@ unconstrained fn test_fail_play_round_too_many_points() { PodRacing::at(contract_address).play_round(game_id, 1, 2, 2, 2, 2, 2) ); } +// docs:end:test-fail-too-many-points // Test: Allocating exactly 9 points is allowed #[test] diff --git a/src/test/utils.nr b/src/test/utils.nr index 726650f..ac62a93 100644 --- a/src/test/utils.nr +++ b/src/test/utils.nr @@ -4,6 +4,7 @@ use ::aztec::{ use crate::PodRacing; +// docs:start:test-setup // Setup function for pod racing contract tests // Returns: (TestEnvironment, contract_address, admin_address) // Note: Create player accounts in individual tests to avoid oracle errors @@ -18,3 +19,4 @@ pub unconstrained fn setup() -> (TestEnvironment, AztecAddress, AztecAddress) { (env, contract_address, admin) } +// docs:end:test-setup diff --git a/src/utils/sponsored_fpc.ts b/src/utils/sponsored_fpc.ts index d599d80..8dc5c65 100644 --- a/src/utils/sponsored_fpc.ts +++ b/src/utils/sponsored_fpc.ts @@ -8,11 +8,13 @@ import type { LogFn } from '@aztec/foundation/log'; import { SponsoredFPCContract, SponsoredFPCContractArtifact } from '@aztec/noir-contracts.js/SponsoredFPC'; import { SPONSORED_FPC_SALT } from '@aztec/constants'; +// docs:start:get-sponsored-fpc export async function getSponsoredFPCInstance(): Promise { return await getContractInstanceFromInstantiationParams(SponsoredFPCContractArtifact, { salt: new Fr(SPONSORED_FPC_SALT), }); } +// docs:end:get-sponsored-fpc export async function getSponsoredFPCAddress() { return (await getSponsoredFPCInstance()).address; diff --git a/yarn.lock b/yarn.lock index abdc2e9..ab28657 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1003,6 +1003,15 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.0" +"@babel/code-frame@^7.21.4": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + "@babel/compat-data@^7.25.2": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb" @@ -1091,6 +1100,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + "@babel/helper-validator-option@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" @@ -1653,6 +1667,18 @@ dependencies: vary "^1.1.2" +"@kwsites/file-exists@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" + integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== + dependencies: + debug "^4.1.1" + +"@kwsites/promise-deferred@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" + integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== + "@lmdb/lmdb-darwin-arm64@3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.3.0.tgz#8120e59facefd54c79b86761ef308db2833a494d" @@ -1759,6 +1785,70 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== +"@npmcli/config@^8.0.0": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@npmcli/config/-/config-8.3.4.tgz#e2712c2215bb2659f39718b23bf7401f9ac1da59" + integrity sha512-01rtHedemDNhUXdicU7s+QYz/3JyV5Naj84cvdXGH4mgCdL+agmSYaLF4LUG4vMCLzhBO8YtS0gPpH1FGvbgAw== + dependencies: + "@npmcli/map-workspaces" "^3.0.2" + "@npmcli/package-json" "^5.1.1" + ci-info "^4.0.0" + ini "^4.1.2" + nopt "^7.2.1" + proc-log "^4.2.0" + semver "^7.3.5" + walk-up-path "^3.0.1" + +"@npmcli/git@^5.0.0": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-5.0.8.tgz#8ba3ff8724192d9ccb2735a2aa5380a992c5d3d1" + integrity sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ== + dependencies: + "@npmcli/promise-spawn" "^7.0.0" + ini "^4.1.3" + lru-cache "^10.0.1" + npm-pick-manifest "^9.0.0" + proc-log "^4.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^4.0.0" + +"@npmcli/map-workspaces@^3.0.2": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.6.tgz#27dc06c20c35ef01e45a08909cab9cb3da08cea6" + integrity sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA== + dependencies: + "@npmcli/name-from-folder" "^2.0.0" + glob "^10.2.2" + minimatch "^9.0.0" + read-package-json-fast "^3.0.0" + +"@npmcli/name-from-folder@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz#c44d3a7c6d5c184bb6036f4d5995eee298945815" + integrity sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg== + +"@npmcli/package-json@^5.1.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-5.2.1.tgz#df69477b1023b81ff8503f2b9db4db4faea567ed" + integrity sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ== + dependencies: + "@npmcli/git" "^5.0.0" + glob "^10.2.2" + hosted-git-info "^7.0.0" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^6.0.0" + proc-log "^4.0.0" + semver "^7.5.3" + +"@npmcli/promise-spawn@^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz#1d53d34ffeb5d151bfa8ec661bcccda8bbdfd532" + integrity sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ== + dependencies: + which "^4.0.0" + "@opentelemetry/api-logs@0.55.0", "@opentelemetry/api-logs@^0.55.0": version "0.55.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.55.0.tgz#5cd7461820d864600250deb3803c32367a6bb2d2" @@ -1961,6 +2051,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.33.0.tgz#ec8ebd2ac768ab366aff94e0e7f27e8ae24fa49f" integrity sha512-TIpZvE8fiEILFfTlfPnltpBaD3d9/+uQHVCyC3vfdh6WfCXKhNFzoP5RyDDIndfvZC5GrA4pyEDNyjPloJud+w== +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -2631,6 +2726,20 @@ resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== +"@types/concat-stream@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-2.0.3.tgz#1f5c2ad26525716c181191f7ed53408f78eb758e" + integrity sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ== + dependencies: + "@types/node" "*" + +"@types/debug@^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" @@ -2638,6 +2747,11 @@ dependencies: "@types/node" "*" +"@types/is-empty@^1.0.0": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/is-empty/-/is-empty-1.2.3.tgz#a2d55ea8a5ec57bf61e411ba2a9e5132fe4f0899" + integrity sha512-4J1l5d79hoIvsrKh5VUKVRA1aIdsOb10Hu5j3J2VfP/msDnfTdGPmNp2E1Wg+vs97Bktzo+MZePFFXSGoykYJw== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" @@ -2665,11 +2779,23 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + "@types/mocha@^10.0.6": version "10.0.7" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== +"@types/ms@*": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" + integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== + "@types/node@*": version "22.5.3" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.3.tgz#91a374e42c6e7ccb5893a87f1775f36ce1671d65" @@ -2684,6 +2810,13 @@ dependencies: undici-types "~6.21.0" +"@types/node@^22.0.0": + version "22.19.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.13.tgz#c03cab3d1f0e5542fa358ecfff08f9b34834884e" + integrity sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw== + dependencies: + undici-types "~6.21.0" + "@types/node@^22.15.1": version "22.15.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.21.tgz#196ef14fe20d87f7caf1e7b39832767f9a995b77" @@ -2706,11 +2839,26 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/supports-color@^8.0.0": + version "8.1.3" + resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.3.tgz#b769cdce1d1bb1a3fa794e35b62c62acdf93c139" + integrity sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg== + +"@types/text-table@^0.2.0": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.5.tgz#f9c609b81c943e9fc8d73ef82ad2f2a78be5f53b" + integrity sha512-hcZhlNvMkQG/k1vcZ6yHOl6WAYftQ2MLfTHcYRZ2xYZFD8tGVnE3qFV0lj1smQeDSR7/yY0PyuUalauf33bJeA== + "@types/tough-cookie@*": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" @@ -2733,6 +2881,11 @@ http-proxy "^1.18.1" ws "^8.13.0" +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + abitype@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.1.0.tgz#510c5b3f92901877977af5e864841f443bf55406" @@ -2811,6 +2964,11 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2835,7 +2993,7 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -anymatch@^3.0.3: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2964,6 +3122,11 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -2979,6 +3142,11 @@ bignumber.js@^9.0.0: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + bintrees@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.2.tgz#49f896d6e858a4a499df85c38fb399b9aff840f8" @@ -3009,7 +3177,14 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.3: +brace-expansion@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -3132,6 +3307,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.0: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + change-case@^5.4.4: version "5.4.4" resolved "https://registry.yarnpkg.com/change-case/-/change-case-5.4.4.tgz#0d52b507d8fb8f204343432381d1a6d7bff97a02" @@ -3142,11 +3322,36 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + +chokidar@^3.0.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +ci-info@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.4.0.tgz#7d54eff9f54b45b62401c26032696eb59c8bd18c" + integrity sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg== + cjs-module-lexer@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz#677de7ed7efff67cc40c9bf1897fea79d41b5215" @@ -3223,6 +3428,11 @@ comlink@^4.4.1: resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981" integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q== +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + command-line-args@^5.1.1: version "5.2.1" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" @@ -3260,6 +3470,16 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + content-disposition@~0.5.2: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -3348,6 +3568,13 @@ debug@4: dependencies: ms "^2.1.3" +debug@^4.0.0, debug@^4.4.0, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" @@ -3355,12 +3582,12 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: dependencies: ms "2.1.2" -debug@^4.4.1: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== +decode-named-character-reference@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz#3e40603760874c2e5867691b599d73a7da25b53f" + integrity sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q== dependencies: - ms "^2.1.3" + character-entities "^2.0.0" dedent@^1.0.0: version "1.5.3" @@ -3411,6 +3638,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + destroy@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -3431,6 +3663,13 @@ detect-node@^2.1.0: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +devlop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -3504,6 +3743,11 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emoji-regex@^10.2.1: + version "10.6.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3526,6 +3770,11 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -3533,6 +3782,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-ex@^1.3.2: + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== + dependencies: + is-arrayish "^0.2.1" + es-define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" @@ -3653,7 +3909,7 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -extend@^3.0.2: +extend@^3.0.0, extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -3738,7 +3994,7 @@ follow-redirects@^1.15.6: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.8.tgz#ae67b97ae32e0a7b36066a5448938374ec18d13d" integrity sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig== -foreground-child@^3.3.1: +foreground-child@^3.1.0, foreground-child@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== @@ -3778,7 +4034,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -3868,6 +4124,25 @@ get-stream@^6.0.0, get-stream@^6.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^10.0.0, glob@^10.2.2: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^13.0.0: version "13.0.1" resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.1.tgz#c59a2500c9a5f1ab9cdd370217ced63c2aa81e60" @@ -4007,6 +4282,13 @@ help-me@^5.0.0: resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== +hosted-git-info@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + html-entities@^2.5.2: version "2.5.3" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.3.tgz#d8a0680bd24ee35af8c74d6d4b695627dde61c00" @@ -4113,6 +4395,11 @@ ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-6.0.2.tgz#77cccb72a55796af1b6d2f9eb14fa326d24f4283" + integrity sha512-InwqeHHN2XpumIkMvpl/DCJVrAHgCsG5+cn1XlnLWGwtZBm8QJfSusItfrwx81CTp5agNZqpKU2J/ccC5nGT4A== + import-local@^3.0.2: version "3.2.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" @@ -4121,11 +4408,24 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +import-meta-resolve@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz#08cb85b5bd37ecc8eb1e0f670dc2767002d43734" + integrity sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg== + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +include_code@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/include_code/-/include_code-0.1.0.tgz#d82a9e4117f79c2863b2c769a484aca48946fe5e" + integrity sha512-a1yMJbLDpqf8TKRo8cnHsTKtwAJ7rxJeYCbz8C7k1l/dhR5y904TqpDQgnOqkYgNZZXY165ga7w1rtsU4aRP7Q== + dependencies: + simple-git "^3.25.0" + unist-util-visit "^5.0.0" + inflation@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/inflation/-/inflation-2.1.0.tgz#9214db11a47e6f756d111c4f9df96971c60f886c" @@ -4144,11 +4444,23 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@^4.1.2, ini@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.3.tgz#4c359675a6071a46985eb39b14e4a2c0ec98a795" + integrity sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -4161,6 +4473,16 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.2" +is-empty@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-empty/-/is-empty-1.2.0.tgz#de9bb5b278738a05a0b09a57e1fb4d4a341a9f6b" + integrity sha512-F2FnH/otLNJv0J6wc73A5Xo7oHLNnqplYqZhUu01tD54DIPvxIRSTSLkrUB/M0nHO4vo1O9PDfN4KoTxCzLh/w== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4178,11 +4500,23 @@ is-generator-function@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -4198,6 +4532,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.5" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.5.tgz#42e368f68d5e10dadfee4fda7b550bc2d8892dc9" + integrity sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w== + isows@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" @@ -4256,6 +4595,15 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jackspeak@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" @@ -4666,12 +5014,17 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz#b43d35e89c0f3be6b5fbbe9dc6c82467b30c28da" + integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ== + json-stringify-deterministic@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/json-stringify-deterministic/-/json-stringify-deterministic-1.0.12.tgz#aaa3f907466ed01e3afd77b898d0a2b3b132820a" integrity sha512-q3PN0lbUdv0pmurkBNdJH3pfFvOTL/Zp0lquqpvcjfKzt6Y0j49EPHAmVHCAS4Ceq/Y+PejWTzyiVpoY71+D6g== -json5@^2.2.3: +json5@^2.0.0, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -4812,6 +5165,11 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lines-and-columns@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" + integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== + lmdb@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-3.3.0.tgz#85149c8178a3fa57a9230bd1adf9fd96ceb9125f" @@ -4831,6 +5189,14 @@ lmdb@^3.2.0: "@lmdb/lmdb-win32-arm64" "3.3.0" "@lmdb/lmdb-win32-x64" "3.3.0" +load-plugin@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/load-plugin/-/load-plugin-6.0.3.tgz#b0eb8ea2361744f0e54850ccbc4c8a2d94ffabe3" + integrity sha512-kc0X2FEUZr145odl68frm+lMJuQ23+rTXYmR6TImqPtbpmXC4vVXbWKDQ9IzndA0HfyQamWfKLhzsqGSTxE63w== + dependencies: + "@npmcli/config" "^8.0.0" + import-meta-resolve "^4.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -4893,6 +5259,16 @@ long@^5.0.0: resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== + +lru-cache@^10.0.1, lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^11.0.0: version "11.1.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.1.0.tgz#afafb060607108132dbc1cf8ae661afb69486117" @@ -4924,11 +5300,64 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +markdown-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz#34bebc83e9938cae16e0e017e4a9814a8330d3c4" + integrity sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== +mdast-util-from-markdown@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz#c95822b91aab75f18a4cbe8b2f51b873ed2cf0c7" + integrity sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + +mdast-util-to-markdown@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4939,6 +5368,200 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +micromark-core-commonmark@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" + integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-destination@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-label@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-space@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-title@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-chunked@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-classify-character@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-combine-extensions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-decode-string@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-html-tag-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== + +micromark-util-normalize-identifier@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-resolve-all@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-subtokenize@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" + integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + +micromark@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" + integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromatch@^4.0.4: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" @@ -5005,11 +5628,23 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.6: +minimatch@^9.0.0, minimatch@^9.0.4: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== + dependencies: + brace-expansion "^2.0.2" + +minimist@^1.0.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" @@ -5103,11 +5738,59 @@ node-releases@^2.0.18: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -normalize-path@^3.0.0: +nopt@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== + dependencies: + abbrev "^2.0.0" + +normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== + dependencies: + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-install-checks@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" + integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== + dependencies: + semver "^7.1.1" + +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== + +npm-package-arg@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-11.0.3.tgz#dae0c21199a99feca39ee4bfb074df3adac87e2d" + integrity sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw== + dependencies: + hosted-git-info "^7.0.0" + proc-log "^4.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + +npm-pick-manifest@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz#83562afde52b0b07cb6244361788d319ce7e8636" + integrity sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA== + dependencies: + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^11.0.0" + semver "^7.3.5" + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -5235,6 +5918,17 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-7.1.1.tgz#68f7e6f0edf88c54ab14c00eb700b753b14e2120" + integrity sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw== + dependencies: + "@babel/code-frame" "^7.21.4" + error-ex "^1.3.2" + json-parse-even-better-errors "^3.0.0" + lines-and-columns "^2.0.3" + type-fest "^3.8.0" + parseurl@^1.3.2: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -5265,6 +5959,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" @@ -5339,7 +6041,12 @@ picocolors@^1.0.0, picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== -picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -5435,6 +6142,11 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +proc-log@^4.0.0, proc-log@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== + process-warning@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.0.tgz#581e3a7a1fb456c5f4fd239f76bce75897682d5a" @@ -5448,6 +6160,19 @@ prom-client@^15.1.3: "@opentelemetry/api" "^1.4.0" tdigest "^0.1.1" +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -5524,7 +6249,15 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -readable-stream@^3.1.1: +read-package-json-fast@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== + dependencies: + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" + +readable-stream@^3.0.2, readable-stream@^3.1.1: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -5533,6 +6266,13 @@ readable-stream@^3.1.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + real-require@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" @@ -5543,6 +6283,45 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +remark-cli@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/remark-cli/-/remark-cli-12.0.1.tgz#991ede01adfdf0471177c381168105da4b93f99a" + integrity sha512-2NAEOACoTgo+e+YAaCTODqbrWyhMVmlUyjxNCkTrDRHHQvH6+NbrnqVvQaLH/Q8Ket3v90A43dgAJmXv8y5Tkw== + dependencies: + import-meta-resolve "^4.0.0" + markdown-extensions "^2.0.0" + remark "^15.0.0" + unified-args "^11.0.0" + +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + +remark@^15.0.0, remark@^15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/remark/-/remark-15.0.1.tgz#ac7e7563260513b66426bc47f850e7aa5862c37c" + integrity sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A== + dependencies: + "@types/mdast" "^4.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -5593,6 +6372,11 @@ retry@0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -5618,6 +6402,11 @@ semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.1.1, semver@^7.3.5: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + semver@^7.5.2: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" @@ -5692,6 +6481,15 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-git@^3.25.0: + version "3.32.3" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.32.3.tgz#1dd6030fd03df4533a9e5a941314335e6265055d" + integrity sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw== + dependencies: + "@kwsites/file-exists" "^1.1.1" + "@kwsites/promise-deferred" "^1.1.1" + debug "^4.4.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -5730,6 +6528,32 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.23" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz#b069e687b1291a32f126893ed76a27a745ee2133" + integrity sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== + split2@^4.0.0, split2@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" @@ -5809,6 +6633,15 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" + integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^10.2.1" + strip-ansi "^7.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -5830,6 +6663,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -5893,6 +6733,11 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" +supports-color@^9.0.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -5940,6 +6785,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + thread-stream@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-3.1.0.tgz#4b2ef252a7c215064507d4ef70c05a5e2d34c4f1" @@ -5974,6 +6824,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +trough@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== + ts-command-line-args@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" @@ -6043,6 +6898,11 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^3.8.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + type-is@^1.6.16, type-is@^1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -6051,6 +6911,11 @@ type-is@^1.6.16, type-is@^1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + typescript@^5.4.4: version "5.5.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" @@ -6083,6 +6948,99 @@ undici@^5.28.5: dependencies: "@fastify/busboy" "^2.0.0" +unified-args@^11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/unified-args/-/unified-args-11.0.1.tgz#5c82564616288b8d99feed7326c2223097d30726" + integrity sha512-WEQghE91+0s3xPVs0YW6a5zUduNLjmANswX7YbBfksHNDGMjHxaWCql4SR7c9q0yov/XiIEdk6r/LqfPjaYGcw== + dependencies: + "@types/text-table" "^0.2.0" + chalk "^5.0.0" + chokidar "^3.0.0" + comma-separated-tokens "^2.0.0" + json5 "^2.0.0" + minimist "^1.0.0" + strip-ansi "^7.0.0" + text-table "^0.2.0" + unified-engine "^11.0.0" + +unified-engine@^11.0.0: + version "11.2.2" + resolved "https://registry.yarnpkg.com/unified-engine/-/unified-engine-11.2.2.tgz#9e2f7e477cc1f431ae5489d67c7363b00b835d7f" + integrity sha512-15g/gWE7qQl9tQ3nAEbMd5h9HV1EACtFs6N9xaRBZICoCwnNGbal1kOs++ICf4aiTdItZxU2s/kYWhW7htlqJg== + dependencies: + "@types/concat-stream" "^2.0.0" + "@types/debug" "^4.0.0" + "@types/is-empty" "^1.0.0" + "@types/node" "^22.0.0" + "@types/unist" "^3.0.0" + concat-stream "^2.0.0" + debug "^4.0.0" + extend "^3.0.0" + glob "^10.0.0" + ignore "^6.0.0" + is-empty "^1.0.0" + is-plain-obj "^4.0.0" + load-plugin "^6.0.0" + parse-json "^7.0.0" + trough "^2.0.0" + unist-util-inspect "^8.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + vfile-reporter "^8.0.0" + vfile-statistics "^3.0.0" + yaml "^2.0.0" + +unified@^11.0.0: + version "11.0.5" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + +unist-util-inspect@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/unist-util-inspect/-/unist-util-inspect-8.1.0.tgz#ff2729b543c483041b3c29cbe04c5460a406ee25" + integrity sha512-mOlg8Mp33pR0eeFpo5d2902ojqFFOKMMG2hF8bmH7ZlhnmjFgh0NI3/ZDwdaBJNbvrS7LZFVrBVtIE9KZ9s7vQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-is@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.1.tgz#d0a3f86f2dd0db7acd7d8c2478080b5c67f9c6a9" + integrity sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz#777df7fb98652ce16b4b7cd999d0a1a40efa3a02" + integrity sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz#9a2a28b0aa76a15e0da70a08a5863a2f060e2468" + integrity sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -6125,11 +7083,70 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== + vary@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vfile-message@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4" + integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile-reporter@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-8.1.1.tgz#ac06a5a68f1b480609c443062dffea1cfa2d11b1" + integrity sha512-qxRZcnFSQt6pWKn3PAk81yLK2rO2i7CDXpy8v8ZquiEOMLSnPw6BMSi9Y1sUCwGGl7a9b3CJT1CKpnRF7pp66g== + dependencies: + "@types/supports-color" "^8.0.0" + string-width "^6.0.0" + supports-color "^9.0.0" + unist-util-stringify-position "^4.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + vfile-sort "^4.0.0" + vfile-statistics "^3.0.0" + +vfile-sort@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vfile-sort/-/vfile-sort-4.0.0.tgz#fa1929065b62fe5311e5391c9434f745e8641703" + integrity sha512-lffPI1JrbHDTToJwcq0rl6rBmkjQmMuXkAxsZPRS9DXbaJQvc642eCg6EGxcX2i1L+esbuhq+2l9tBll5v8AeQ== + dependencies: + vfile "^6.0.0" + vfile-message "^4.0.0" + +vfile-statistics@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-3.0.0.tgz#0f5cd00c611c1862b13a9b5bc5599efaf465f2cf" + integrity sha512-/qlwqwWBWFOmpXujL/20P+Iuydil0rZZNglR+VNm6J0gpLHwuVM5s7g2TfVoswbXjZ4HuIhLMySEyIw5i7/D8w== + dependencies: + vfile "^6.0.0" + vfile-message "^4.0.0" + +vfile@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + "viem@npm:@aztec/viem@2.38.2": version "2.38.2" resolved "https://registry.yarnpkg.com/@aztec/viem/-/viem-2.38.2.tgz#9c626b46569cce1f30f08f7be23fb73846aea520" @@ -6144,6 +7161,11 @@ vary@^1.1.2: ox "0.9.6" ws "8.18.3" +walk-up-path@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-3.0.1.tgz#c8d78d5375b4966c717eb17ada73dbd41490e886" + integrity sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA== + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -6176,6 +7198,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + wordwrapjs@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" @@ -6249,6 +7278,11 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yaml@^2.0.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" + integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== + yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" @@ -6286,3 +7320,8 @@ zod@^3.23.8: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== + +zwitch@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==