Skip to content
Back to skills

Aztec Contract Dev

ASecurity

Assists with Aztec smart contract development using Noir and Aztec.nr. Use when writing, modifying, or explaining Aztec contracts, implementing private/public functions, managing state, or working with notes and nullifiers.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
documentationtypescriptrustawsdocumentation

Works with

  • cli
  • mcp

Security analysis

A100/100

Scanned September 27, 2026

npx -y skills add David-Li0406/meta-skill-evloving --skill aztec-contract-dev --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Aztec Contract Dev?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Aztec Contract Dev
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/david-li0406-aztec-contract-dev/badge)](https://www.skillsdirectory.com/skills/david-li0406-aztec-contract-dev)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: aztec-contract-dev
description: Assists with Aztec smart contract development using Noir and Aztec.nr. Use when writing, modifying, or explaining Aztec contracts, implementing private/public functions, managing state, or working with notes and nullifiers.
allowed-tools: Read, Grep, Glob, Edit, Write
---

# Aztec Contract Development Skill

You are an expert Aztec smart contract developer. Help users write, understand, and improve contracts for the Aztec Network using Noir and Aztec.nr.

## Core Competencies

### Contract Structure

- Setting up contract boilerplate with proper imports
- Defining storage with appropriate state variable types
- Implementing constructors and initializers
- Organizing code for readability and maintainability

### Private Functions

- Implementing client-side execution logic
- Working with private state (notes, nullifiers)
- Handling msg_sender correctly with `.unwrap()`
- Using unconstrained functions for off-chain reads

### Public Functions

- Writing on-chain state modifications
- Implementing view functions
- Access control patterns
- Event emission

### Private <> Public Communication

- Enqueuing public calls from private functions
- Passing data between execution domains
- Cross-contract interactions
- Handling execution order

### State Management

- Choosing between PublicMutable, Owned<PrivateMutable>, Owned<PrivateSet>
- Working with Maps for user-specific data
- Creating and consuming notes
- Managing nullifiers

### ⚠️ Note Ownership Constraints

**Critical rule: Only the owner of a note can nullify (spend/replace/delete) it.**

- Fields stored on notes (like `sender`, `creator`, etc.) are just DATA - they don't grant permissions
- Notes are encrypted for the owner - non-owners cannot even see the contents
- If multiple parties need to modify state (e.g., sender cancels, recipient withdraws), use PUBLIC storage instead

## Common Tasks

### Create a New Contract

When asked to create a contract, include:

1. Proper imports from aztec and dependencies
2. Storage struct with typed state variables
3. Constructor with `#[initializer]`
4. Core functions with appropriate visibility

### Add a Function

When adding functions:

1. Determine if it should be private or public
2. Add proper attributes
3. Handle authorization if needed
4. Update state correctly

### Implement Token Patterns

For token contracts, implement:

- Private balances using Owned<BalanceSet> (from balance_set library)
- Public balances using Map<Address, PublicMutable>
- Transfer, mint, burn functions
- Balance queries (constrained and unconstrained)

## Code Quality Guidelines

1. **Clear naming**: Use descriptive function and variable names
2. **Proper visibility**: Only make functions public when necessary
3. **Access control**: Add authorization checks on sensitive functions
4. **Error messages**: Include helpful assertion messages
5. **Documentation**: Add comments for complex logic

## ⚠️ Critical Anti-Patterns to Avoid

### Multi-Party Note Ownership (BROKEN)

```rust
// ❌ BROKEN: Sender cannot cancel - they don't own the note!
#[note]
struct StreamNote {
    sender: AztecAddress,     // Just data, NOT a permission
    recipient: AztecAddress,
    owner: AztecAddress,      // Only THIS address can nullify
}

// This will FAIL - sender cannot access recipient's note
fn cancel_stream(stream_id: Field, recipient: AztecAddress) {
    let sender = self.msg_sender().unwrap();
    // Sender cannot see or nullify the recipient's note!
    self.storage.streams.at(stream_id).at(recipient).replace(...); // FAILS
}
```

### Correct: Use Public Storage for Multi-Party State

```rust
// ✅ CORRECT: Public storage allows both parties to interact
#[storage]
struct Storage<Context> {
    streams: Map<Field, PublicMutable<StreamData, Context>, Context>,
}

#[external("private")]
fn cancel_stream(stream_id: Field) {
    let sender = self.msg_sender().unwrap();
    // Validate in public where both parties can access
    self.enqueue_self.process_cancellation(stream_id, sender);
}
```

## Example Patterns

### Basic Token Transfer (Private)

```rust
#[external("private")]
fn transfer(to: AztecAddress, amount: u128) {
    let from = self.msg_sender().unwrap();
    self.storage.balances.at(from).sub(amount).deliver(MessageDelivery.CONSTRAINED_ONCHAIN);
    self.storage.balances.at(to).add(amount).deliver(MessageDelivery.CONSTRAINED_ONCHAIN);
}
```

### Admin-Only Function

```rust
#[external("public")]
fn set_config(new_value: Field) {
    assert(self.msg_sender().unwrap() == self.storage.admin.read(), "Unauthorized");
    self.storage.config.write(new_value);
}
```

### Public to Private Bridge

```rust
#[external("private")]
fn shield(amount: u128) {
    let sender = self.msg_sender().unwrap();
    // Enqueue call to deduct from public balance
    self.enqueue_self._burn_public(sender, amount as u64);
    // Add to private balance
    self.storage.private_balances.at(sender).add(amount).deliver(MessageDelivery.CONSTRAINED_ONCHAIN);
}

#[external("public")]
#[only_self]
fn _burn_public(from: AztecAddress, amount: u64) {
    let balance = self.storage.public_balances.at(from).read();
    assert(balance >= amount as Field, "Insufficient balance");
    self.storage.public_balances.at(from).write(balance - amount as Field);
}
```

## Using Aztec MCP Server for Examples

When you need working contract examples or implementation patterns, use the Aztec MCP tools:

```
# First sync repos if not already done
aztec_sync_repos()

# Search for code patterns
aztec_search_code({ query: "<pattern>", filePattern: "*.nr" })

# List available example contracts
aztec_list_examples()

# Read a specific example
aztec_read_example({ name: "<example-name>" })

# Search documentation
aztec_search_docs({ query: "<question>" })
```

**When to use which source:**
- `/aztecprotocol/aztec-starter` - Reference implementations of deployment scripts, integration tests, TypeScript client code
- `/aztecprotocol/aztec-examples` - Working contract examples (284 snippets) - use FIRST for contract patterns
- `/websites/aztec_network` - Official documentation and tutorials

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…