Agent Skills: Aztec Developer Skill

Patterns for Aztec development: contracts, frontend, testing. Use when working with Aztec contracts in any capacity unless otherwise specified

UncategorizedID: critesjosh/aztec-claude-plugin/aztec-developer

Install this agent skill to your local

pnpm dlx add-skill https://github.com/critesjosh/aztec-claude-plugin/tree/HEAD/skills/aztec-developer

Skill Files

Browse the full folder contents for aztec-developer.

Download Skill

Loading file tree…

skills/aztec-developer/SKILL.md

Skill Metadata

Name
aztec-developer
Description
"Aztec smart contract development, Noir programming, testing, deployment, and TypeScript integration. Use when working with Aztec contracts, notes, private state, or any Aztec SDK code. Use review-contract for security reviews."

Aztec Developer Skill

Framework knowledge for Aztec smart contract development that is NOT obvious from reading source code.

Common Hallucinations to Avoid

These are facts Claude frequently gets wrong about Aztec. Consult this before making claims:

Noir Integer Overflow

Noir integer types (u8, u64, u128) PANIC on overflow — they do NOT wrap. Only Field arithmetic wraps (around the field modulus). This means u64 addition is already overflow-safe in Noir — no explicit guard is needed. However, Field should never be used for amounts that need overflow protection.

The #[note] Macro Injects Randomness

The #[note] attribute macro automatically injects a NoteHeader field containing a nonce for commitment uniqueness. You do NOT need to add a manual randomness field to note structs. A note with only amount and owner fields is valid — the macro handles the rest.

Owned<PrivateSet<T>> Requires Double .at()

For storage declared as Map<AztecAddress, Owned<PrivateSet<NoteType>>>:

self.storage.private_balances.at(owner_address).at(owner_address).insert(note)

The first .at() indexes the Map by key. The second .at() authenticates the Owned wrapper with the owner. This is correct, not a bug.

pop_notes vs get_notes

  • get_notes() reads notes without nullifying them — notes remain in the database after the call
  • pop_notes() reads and nullifies in one step — the correct choice when consuming notes (e.g., spending a balance)
  • If you use get_notes() to read notes you intend to consume, you must manually nullify them or they can be re-read

enqueue vs enqueue_incognito

  • self.enqueue(...) — the msg_sender of the enqueued public call is visible on-chain
  • self.enqueue_incognito(...) — hides the msg_sender in the public call
  • Use enqueue_incognito when the shield or private→public boundary should not reveal who initiated the action

Token Amounts: Use u128, Not u64

u64 max is ~18.4 × 10¹⁸. With 18 decimal places (standard), this limits total supply to ~18.4 tokens. Always use u128 for token amounts to match ERC-20 semantics. The standard Aztec note type for balances is UintNote (from uint_note crate) which stores value: u128.

self.msg_sender() vs self.context.maybe_msg_sender()

  • self.msg_sender() — returns AztecAddress directly, panics if sender is None
  • self.context.maybe_msg_sender() — returns Option<AztecAddress>, safe for entrypoints and incognito calls
  • msg_sender is None at: (1) tx entrypoints (account contracts), (2) public calls via enqueue_incognito()
  • msg_sender in enqueued public calls is visible on-chain — this is a common privacy leak

Account Deployment Uses AztecAddress.ZERO

When deploying an account contract, the account doesn't exist on-chain yet, so it can't be the sender. Use from: AztecAddress.ZERO for the deployment transaction.

Always Simulate Before Send

Call .simulate() before .send() for every state-changing transaction. Simulation runs locally and surfaces revert reasons immediately. Without it, a failing transaction hangs until the send timeout (up to 600s) with an opaque error.

Subskills

Key Concepts

| Concept | What It Means in Aztec | |---------|----------------------| | Notes | Encrypted UTXOs — the only way to store private state. Only the owner can nullify. | | Nullifiers | On-chain markers that "spend" a note without revealing which one. Prevents double-spend. | | Enqueue | Private functions can't directly modify public state — they enqueue public calls for the sequencer. | | Owned<T> | Wrapper that ties a private state variable to a specific owner. Required for private sets/maps. |

Storage Types

| Need | Use | |------|-----| | Public value anyone can read/write | PublicMutable<T> | | Private value only owner accesses | Owned<PrivateMutable<T>> | | Private set of notes (e.g., balances) | Owned<PrivateSet<T>> | | Per-user storage | Map<AztecAddress, T> | | Public value readable from private (with delay) | DelayedPublicMutable<T> |

Using Aztec MCP Server

For API docs and code examples beyond what's here, use:

aztec_sync_repos()                                           # sync first
aztec_search_code({ query: "<pattern>", filePattern: "*.nr" })
aztec_list_examples()
aztec_search_docs({ query: "<question>" })