Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ Therefore it is recommended to read the `aztec-nr` [guide on authwitnesses](../a

The authwit system supports different intent types depending on your use case:

- **`CallIntent`**: Use when authorizing a specific contract function call. Contains `{ caller, action }` where `action` is a `ContractFunctionInteraction`.
- **`CallIntent`**: Use when authorizing a specific contract function call. Contains `{ caller, call }` where `call` is a `FunctionCall`, typically obtained with `await interaction.getFunctionCall()`.
- **`ContractFunctionInteractionCallIntent`**: Convenience form that takes the interaction directly. Contains `{ caller, action }` where `action` is a `ContractFunctionInteraction`; internally resolved to a `FunctionCall` before signing.
- **`IntentInnerHash`**: Use when authorizing arbitrary data. Contains `{ consumer, innerHash }` where `consumer` is the contract that will verify the authwit.

## Create private authwits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Log values from your contract using `debug_log`:

```rust
// Import debug logging
use dep::aztec::oracle::debug_log::{ debug_log, debug_log_format };
use aztec::oracle::logging::{ debug_log, debug_log_format };

// Log simple messages
debug_log("checkpoint reached");
Expand Down Expand Up @@ -65,23 +65,20 @@ LOG_LEVEL="info;debug:simulator:client_execution_context;debug:simulator:client_
```

:::info Log filter format
`LOG_LEVEL` accepts a semicolon-delimited list of filters. Each filter can be:

- `level` - Sets default level for all modules
- `level:module` - Sets level for a specific module
- `level:module:submodule` - Sets level for a specific submodule
`LOG_LEVEL` is a semicolon-delimited list. The **first segment must be a bare log level** — it sets the default level for all modules. Subsequent segments are `level:module` (or `level:module:submodule`) overrides.

```bash
# Default level only
LOG_LEVEL="debug"

# Default level + specific module overrides
# Default level + module overrides
LOG_LEVEL="info;debug:simulator;debug:execution"

# Default level + specific submodule overrides
# Default level + submodule overrides
LOG_LEVEL="info;debug:simulator:client_execution_context;debug:simulator:client_view_context"
```

A bare `level:module` (e.g. `LOG_LEVEL="warn:simulator"`) is **not valid** — the parser reads the first segment as the default level and rejects it with `Invalid log level: warn:simulator`.
:::

## Debugging common errors
Expand Down Expand Up @@ -113,9 +110,12 @@ LOG_LEVEL="info;debug:simulator:client_execution_context;debug:simulator:client_
### Quick Fixes for Common Issues

```bash
# Archiver sync issues - force progress with dummy transactions
aztec-wallet send transfer --from test0 --to test0 --amount 0
aztec-wallet send transfer --from test0 --to test0 --amount 0
# Archiver sync issues - force progress with dummy transactions.
# Assumes you have imported the local network test accounts
# (aztec-wallet import-test-accounts) and have a deployed token
# aliased as `testtoken`.
aztec-wallet send transfer --from test0 --contract-address testtoken --args accounts:test0 0
aztec-wallet send transfer --from test0 --contract-address testtoken --args accounts:test0 0

# L1 to L2 message pending - wait for inclusion
# Messages need 2 blocks to be processed
Expand Down Expand Up @@ -207,7 +207,7 @@ LOG_LEVEL=verbose aztec start --local-network
### Common debug imports

```rust
use dep::aztec::oracle::debug_log::{ debug_log, debug_log_format };
use aztec::oracle::logging::{ debug_log, debug_log_format };
```

### Check contract registration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ High-level structure of how Aztec smart contracts including the different compon

## Directory structure

Here's a common layout for a basic Aztec.nr Contract project:
When you create a new project with `aztec new my_project`, it generates a single-crate Noir contract project:

```text title="layout of an aztec contract project"
─── my_aztec_contract_project
├── src
└── main.nr <-- your contract
└── Nargo.toml <-- package and dependency management
─── my_project
├── Nargo.toml <-- contract package and dependencies
└── src
└── main.nr <-- your contract
```

`Nargo.toml` declares the contract package (with `type = "contract"`) and its dependencies. Your contract code lives in `src/main.nr`. Noir tests using `#[test]` live alongside the contract in the same crate — see [Testing Contracts](../testing_contracts.md).

To add another contract as a sibling of an existing one, run `aztec new <name>` from the parent directory (each contract is its own crate). To initialize a contract project inside an existing empty directory instead, `cd` into it and run `aztec init` (it takes no positional argument; pass `--name <name>` if you want the package name to differ from the directory name).

See the vanilla Noir docs for [more info on packages](https://noir-lang.org/docs/noir/modules_packages_crates/crates_and_packages).

## Contract block
Expand All @@ -44,7 +48,7 @@ The `#[aztec]` macro performs a lot of the low-level operations required to take

## Imports

Aside from the [`#[aztec]`](pathname:///aztec-nr-api/testnet/noir_aztec/macros/aztec/fn.aztec) macro import, all other imports need to go _inside_ the `contract` block - this is because `contract` acts like `mod`, creating a new [module](https://noir-lang.org/docs/noir/modules_packages_crates/modules).
Aside from the [`#[aztec]`](pathname:///aztec-nr-api/mainnet/noir_aztec/macros/fn.aztec) macro import, all other imports need to go _inside_ the `contract` block - this is because `contract` acts like `mod`, creating a new [module](https://noir-lang.org/docs/noir/modules_packages_crates/modules).

```rust
use aztec::macros::aztec;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ Create constructor-like functions using the `#[initializer]` annotation:
#[initializer]
#[external("private")]
// We can name our initializer anything we want as long as it's marked as aztec(initializer)
fn initialize(headstart: u64, owner: AztecAddress) {
self.storage.counters.at(owner).add(headstart as u128).deliver(
fn initialize(headstart: u128, owner: AztecAddress) {
self.storage.counters.at(owner).add(headstart).deliver(
MessageDelivery.ONCHAIN_CONSTRAINED,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,15 @@ storage.votes.insert(new_vote).deliver(vote_counter); // the vote counter accoun

### Flow

1. Write your contract and specify your contract dependencies. Every contract written for Aztec will have
aztec-nr as a dependency. Add it to your `Nargo.toml` with
1. Write your contract and specify your contract dependencies. Create a new project with `aztec new my_project`, which creates a single-crate Noir contract project (`Nargo.toml` + `src/main.nr`) with the `aztec` dependency already configured. If you need additional dependencies, add them to `my_project/Nargo.toml`:

```toml
# Nargo.toml
# my_project/Nargo.toml
[dependencies]
aztec = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.1.0-rc.2", directory="aztec" }
```

Update your `main.nr` contract file to use the Aztec.nr macros for writing contracts.
Update your `my_project/src/main.nr` contract file to use the Aztec.nr macros for writing contracts.

```rust title="setup" showLineNumbers
use aztec::macros::aztec;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,28 @@ Always use `aztec test` instead of `nargo test`. The `TestEnvironment` requires

## Basic test structure

Tests live in the same crate as your contract. `aztec new` creates a single-crate project, and the convention is to place `#[test]` functions in a `mod tests` block alongside the contract (or in submodules of the crate):

```rust
use crate::MyContract;
use aztec::{
protocol::address::AztecAddress,
test::helpers::test_environment::TestEnvironment,
};
use aztec::macros::aztec;

#[test]
unconstrained fn test_basic_flow() {
// 1. Create test environment
let mut env = TestEnvironment::new();
#[aztec]
pub contract MyContract {
// ...contract functions...
}

// 2. Create accounts
let owner = env.create_light_account();
mod tests {
use super::MyContract;
use aztec::test::helpers::test_environment::TestEnvironment;

#[test]
unconstrained fn test_basic_flow() {
// 1. Create test environment
let mut env = TestEnvironment::new();

// 2. Create accounts
let _owner = env.create_light_account();
}
}
```

Expand All @@ -72,13 +80,14 @@ unconstrained fn test_basic_flow() {
:::

:::tip Organizing test files
You can organize tests in separate files:
For larger test suites, split tests into submodules of your crate rather than keeping them all inside `main.nr`:

- Create `src/test.nr` with `mod utils;` to import helper functions
- Split tests into modules like `src/test/transfer_tests.nr`, `src/test/auth_tests.nr`
- Import the test module in `src/main.nr` with `mod test;`
- Share setup functions in `src/test/utils.nr`
:::
- Create modules like `src/transfer_tests.nr`, `src/auth_tests.nr`
- Declare them from `src/main.nr` with `mod transfer_tests;`, `mod auth_tests;`
- Share setup functions in `src/test_utils.nr`

See the [aztec-standards token contract](https://github.com/defi-wonderland/aztec-standards/tree/dev/src/token_contract) for a worked example of this layout.
:::

## Deploying contracts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ Let’s create a constructor method to run on deployment that assigns an initial
#[initializer]
#[external("private")]
// We can name our initializer anything we want as long as it's marked as aztec(initializer)
fn initialize(headstart: u64, owner: AztecAddress) {
self.storage.counters.at(owner).add(headstart as u128).deliver(
fn initialize(headstart: u128, owner: AztecAddress) {
self.storage.counters.at(owner).add(headstart).deliver(
MessageDelivery.ONCHAIN_CONSTRAINED,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ nargo test
Expected output:

```text
[hello_circuit] Running 1 test functions
[hello_circuit] Testing test_main... ok
[hello_circuit] All tests passed
[hello_circuit] Running 1 test function
[hello_circuit] Testing test_main ... ok
[hello_circuit] 1 test passed
```

**Tip**: Circuit tests run without generating proofs, making them fast for development. Use them to verify your circuit logic before the more expensive proof generation step.
Expand All @@ -228,9 +228,11 @@ The contract demonstrates several important patterns:
Use `aztec new` to generate the contract project structure:

```bash
aztec new contract --name ValueNotEqual
aztec new --name ValueNotEqual contract
```

The `aztec new` wrapper stops parsing arguments at the first positional, so `--name` must come **before** the `contract` path — otherwise the flag is silently dropped and the Nargo package ends up named `contract`. The Nargo package name (`--name`) is independent of the Noir contract name declared inside `main.nr`; the artifact filename downstream is driven by the contract name.

This creates:

```tree
Expand Down Expand Up @@ -701,7 +703,7 @@ Expected output:
Proof verification: SUCCESS
Using deflattenFields to convert proof...
VK size: 115
Proof size: 508
Proof size: 500
Public inputs: 1
Done
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ We'll create BOB tokens with:
Let's create a simple yarn + aztec.nr project:

```bash
mkdir bob_token_contract
cd bob_token_contract
yarn init
aztec new bob_token
cd bob_token
yarn init -y
# This is to ensure yarn uses node_modules instead of pnp for dependency installation
yarn config set nodeLinker node-modules
yarn add @aztec/aztec.js@v4.1.0-rc.2 @aztec/accounts@v4.1.0-rc.2 @aztec/test-wallet@v4.1.0-rc.2 @aztec/kv-store@v4.1.0-rc.2
Expand Down Expand Up @@ -90,6 +90,10 @@ pub contract BobToken {

These are the different macros we need to define the visibility of functions, and some handy types and functions.

:::note
You may see "unused import" warnings from your IDE or compiler for `only_self`, `MessageDelivery`, and `Owned`. That's expected at this stage — we'll start using them in Part 2 when we add the private half of the contract.
:::

## Building the Mental Health Token System

### The Privacy Architecture
Expand Down Expand Up @@ -270,7 +274,9 @@ async function main() {
// Connect to local network
const node = createAztecNodeClient("http://localhost:8080");

const wallet = await EmbeddedWallet.create(node);
// `ephemeral: true` keeps PXE state in memory, so restarting the local
// network won't leave this script pointing at stale block hashes.
const wallet = await EmbeddedWallet.create(node, { ephemeral: true });

const [giggleWalletData, aliceWalletData, bobClinicWalletData] =
await getInitialTestAccountsData();
Expand Down Expand Up @@ -319,6 +325,14 @@ What's this `tsx` dark magic? Well, it just compiles and runs typescript using r

:::

:::tip Ephemeral PXE state

We pass `{ ephemeral: true }` to `EmbeddedWallet.create`. This tells the PXE to keep its state in memory instead of writing it to `pxe_data_*` / `wallet_data_*` folders on disk. If you ever stop and restart your local network (or wipe its state), the next run starts clean instead of failing with errors like `No local block hash for block number …` because on-disk PXE state no longer matches the chain.

For real applications you typically want persistent state, but for tutorials that spin up a fresh network each run, ephemeral is the safer default.

:::

### 🎉 Celebrate

Congratulations! You've just deployed a working token contract on Aztec! You can:
Expand Down Expand Up @@ -353,11 +367,15 @@ When Alice spends 40 BOB tokens at Bob's clinic:
3. She creates a "change" note for herself (40 BOB)
4. The consumed notes are nullified (marked as spent)

:::info What is a nullifier?
A **nullifier** is a unique, one-way tag emitted when a private note is spent. The network adds it to a nullifier tree so the same note can't be spent twice, but because the nullifier is derived from secrets only the note's owner knows, nobody can link a nullifier back to the note it invalidated. See [State Management](../../foundational-topics/state_management.md#private-state) for more.
:::

In this case, all that the network sees (including Giggle) is just "something happening to some state in some contract". How cool is that?

### Updating Storage for Privacy

For something like balances, you can use a simple library called `easy_private_state` which abstracts away a custom private Note. A Note is at the core of how private state works in Aztec and you can read about it [here](../../foundational-topics/state_management.md). For now, let's just import the library in `Nargo.toml`:
For something like balances, you can use a simple library called `balance_set` which abstracts away a custom private Note. A Note is at the core of how private state works in Aztec and you can read about it [here](../../foundational-topics/state_management.md). For now, let's add it by replacing the `[dependencies]` section in `Nargo.toml`:

```toml
[dependencies]
Expand Down Expand Up @@ -580,6 +598,14 @@ aztec codegen target --outdir artifacts

## Testing the Complete Privacy System

Before running the updated script, double-check your local network is still running:

```bash
aztec start --local-network
```

If you stopped it between parts of the tutorial, start it again here. Because we set `ephemeral: true` when creating the wallet, restarting the network is safe — the script won't try to reuse stale PXE state from a previous run.

Now that you've implemented all the privacy features, let's update our test script to showcase the full privacy flow:

### Update Your Test Script
Expand Down Expand Up @@ -680,7 +706,14 @@ Let's give it a try:
npx tsx index.ts
```

You should see the complete privacy journey from transparent allocation to confidential usage!
You should see the complete privacy journey from transparent allocation to confidential usage. The final pair of log lines should look like:

```text
📊 Alice has 100 public BOB tokens and 130 private BOB tokens
📊 Bob's Clinic has 20 public BOB tokens and 50 private BOB tokens
```

If your output doesn't match, double-check that the local network is running and that you started this run with a fresh `aztec start --local-network`.

## Summary

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,14 @@ This can present confusion when opening older contracts (and dependencies) writt

### Steps to keep up to date

1. Update the Aztec local network to the latest version (includes `aztec` command, pxe, etc):
1. Update the Aztec local network to a specific version (includes `aztec` command, pxe, etc) — `aztec-up` uses explicit subcommands to install and switch between versions. For example, to install and use [v**0.77.0**](https://github.com/AztecProtocol/aztec-packages/tree/v0.77.0):

```shell
aztec-up
aztec-up install 0.77.0
aztec-up use 0.77.0
```

To update to a specific version, pass the version number after the `aztec-up` command, or set `VERSION` for a particular git tag, eg for [v**0.77.0**](https://github.com/AztecProtocol/aztec-packages/tree/v0.77.0)

```shell
aztec-up 0.77.0
# or
VERSION=0.77.0 aztec-up
```
Run `aztec-up list` to see installed versions, or `aztec-up --help` for all available subcommands.

2. Update Aztec.nr and individual @aztec dependencies:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,13 @@ aztec get-l1-addresses \

```bash
aztec debug-rollup \
--rollup $NEW_ROLLUP_ADDRESS \
--l1-rpc-urls $L1_RPC_URL \
-c $L1_CHAIN_ID
```

The `--rollup` flag is required; without it the command may fail trying to resolve the default rollup address.

---

## Quick Test (Empty Payload)
Expand Down
Loading
Loading