Writing Contracts
Overview
To write a contract:
- Import aztec.nr and declare your contract
mod test;
use dep::aztec::macros::aztec;
#[aztec]
contract EasyPrivateVoting {
- Define imports in your contract block
use dep::aztec::{
keys::getters::get_public_keys,
macros::{functions::{initializer, internal, private, public}, storage::storage},
};
use dep::aztec::prelude::{AztecAddress, Map, PublicImmutable, PublicMutable};
Source code: noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr#L8-L14
- Declare your contract storage below your imports
#[storage]
struct Storage<Context> {
admin: PublicMutable<AztecAddress, Context>, // admin can end vote
tally: Map<Field, PublicMutable<Field, Context>, Context>, // we will store candidate as key and number of votes as value
vote_ended: PublicMutable<bool, Context>, // vote_ended is boolean
active_at_block: PublicImmutable<u32, Context>, // when people can start voting
}
Source code: noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr#L15-L23
- Declare a constructor with
#[initializer]
. Constructors can be private or public functions.
#[public]
#[initializer]
// annotation to mark function as a constructor
fn constructor(admin: AztecAddress) {
storage.admin.write(admin);
storage.vote_ended.write(false);
storage.active_at_block.initialize(context.block_number() as u32);
}
Source code: noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr#L25-L34
- Declare your contract functions
#[private]
// annotation to mark function as private and expose private context
fn cast_vote(candidate: Field) {
let msg_sender_npk_m_hash = get_public_keys(context.msg_sender()).npk_m.hash();
let secret = context.request_nsk_app(msg_sender_npk_m_hash); // get secret key of caller of function
let nullifier = std::hash::pedersen_hash([context.msg_sender().to_field(), secret]); // derive nullifier from sender and secret
context.push_nullifier(nullifier);
EasyPrivateVoting::at(context.this_address()).add_to_tally_public(candidate).enqueue(
&mut context,
);
}
Source code: noir-projects/noir-contracts/contracts/easy_private_voting_contract/src/main.nr#L36-L49
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.
Section Contents
📄️ Defining Initializer Functions
This page explains how to write an initializer function, also known as a constructor.
🗃️ Declaring Storage
2 items
🗃️ Notes
3 items
📄️ Calling Other Contracts
A contract is a collection of persistent state variables and functions which may manipulate these variables.
📄️ Emitting Events
Events in Aztec work similarly to Ethereum events in the sense that they are a way for contracts to communicate with the outside world.
📄️ Using the Archive Tree
The Aztec Protocol uses an append-only Merkle tree to store hashes of the headers of all previous blocks in the chain as its leaves. This is known as the Archive tree. You can learn more about how it works in the concepts section.
📄️ Using the popCapsule Oracle
popCapsule is used for passing artbitrary data. We have not yet included this in Aztec.nr, so it is a bit more complex than the other oracles. You can follow this how-to:
📄️ Common Patterns
There are many common patterns have been devised by the Aztec core engineering team and the work of the external community as we build Aztec.nr contracts internally (see some of them here (GitHub link)).
🗃️ Portals
1 item
📄️ Authentication Witness
Developer Documentation to use Authentication Witness for authentication actions on Aztec.