Skip to main content
Version: dev

Project Structure

This guide explains how to set up and structure your project for Aztec smart contracts.

Before you start

You should have installed the sandbox, which includes local development tools, as described in the getting started guide.

Setup

To create a new project, run the following command:

aztec-nargo new new_project --contract

This will create a new project with a Nargo.toml file and a src directory with a main.nr file where your contract will be written.

Dependencies

Define Aztec.nr as a dependency in your Nargo.toml file. Aztec.nr is a package that contains the core functionality for writing Aztec smart contracts.

[dependencies]
aztec = { git = "https://github.com/AztecProtocol/aztec-packages", tag = "master", directory = "noir-projects/aztec-nr/aztec" }

Writing a contract

To write a contract:

  1. Import aztec.nr into your contract in the src/main.nr file and declare your contract
use dep::aztec::macros::aztec;

#[aztec]
pub contract Counter {
  1. Define imports in your contract block

For example, these are the imports for the example counter contract:

imports
use aztec::{
macros::{functions::{initializer, private, public, utility}, storage::storage},
oracle::debug_log::debug_log_format,
protocol_types::{address::AztecAddress, traits::ToField},
state_vars::Map,
};
use easy_private_state::EasyPrivateUint;
Source code: noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr#L7-L15
  1. Declare your contract storage below your imports
storage_struct
#[storage]
struct Storage<Context> {
counters: Map<AztecAddress, EasyPrivateUint<Context>, Context>,
}
Source code: noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr#L17-L22
  1. Declare a constructor with #[initializer]. Constructors can be private or public functions.
constructor
#[initializer]
#[private]
// We can name our initializer anything we want as long as it's marked as aztec(initializer)
fn initialize(headstart: u64, owner: AztecAddress) {
let counters = storage.counters;
counters.at(owner).add(headstart, owner);
}
Source code: noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr#L24-L32
  1. Declare your contract functions
increment
#[private]
fn increment(owner: AztecAddress) {
debug_log_format("Incrementing counter for owner {0}", [owner.to_field()]);

Counter::at(context.this_address()).emit_in_public(12345).enqueue(&mut context);

let counters = storage.counters;
counters.at(owner).add(1, owner);
}
Source code: noir-projects/noir-contracts/contracts/test/counter_contract/src/main.nr#L34-L44

There is a lot more detail and nuance to writing contracts, but this should give you a good starting point. Read contents of this section for more details about authorizing contract to act on your behalf (authenticaion witnesses), emitting events, calling functions on other contracts and other common patterns.