Skip to main content

Writing Contracts

Overview

To write a contract:

  1. Import aztec.nr and declare your contract
mod test;
use dep::aztec::macros::aztec;

#[aztec]
contract EasyPrivateVoting {
  1. Define imports in your contract block
imports
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
  1. Declare your contract storage below your imports
storage_struct
#[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
  1. Declare a constructor with #[initializer]. Constructors can be private or public functions.
constructor
#[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
  1. Declare your contract functions
cast_vote
#[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