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:
- 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 {
- Define imports in your contract block
For example, these are the imports for the example counter contract:
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
- Declare your contract storage below your imports
#[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
- Declare a constructor with
#[initializer]
. Constructors can be private or public functions.
#[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
- Declare your contract functions
#[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.