Skip to content

Commit 459e929

Browse files
critesjoshAztecBot
authored andcommitted
cherry-pick: 49e2563 fix(docs): update CLI commands, ABI fields, and tutorial fixes (with conflicts)
1 parent d79af8a commit 459e929

10 files changed

Lines changed: 3562 additions & 7 deletions

File tree

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
---
2+
title: Counter Contract
3+
description: Code-along tutorial for creating a simple counter contract on Aztec.
4+
sidebar_position: 0
5+
references: ["docs/examples/contracts/counter_contract/src/main.nr"]
6+
---
7+
8+
import Image from "@theme/IdealImage";
9+
10+
In this guide, we will create our first Aztec.nr smart contract. We will build a simple private counter, where you can keep your own private counter - so no one knows what ID you are at or when you increment! This contract will get you started with the basic setup and syntax of Aztec.nr, but doesn't showcase all of the awesome stuff Aztec is capable of.
11+
12+
This tutorial is compatible with the Aztec version `4.2.0-aztecnr-rc.2`. Install the correct version with `VERSION=4.2.0-aztecnr-rc.2 bash -i <(curl -sL https://install.aztec.network/4.2.0-aztecnr-rc.2)`. Or if you'd like to use a different version, you can find the relevant tutorial by clicking the version dropdown at the top of the page.
13+
14+
## Prerequisites
15+
16+
- You have followed the [quickstart](../../../getting_started_on_local_network.md)
17+
- Running Aztec local network
18+
- Installed [Noir LSP](../../aztec-nr/installation.md) (optional)
19+
20+
## Set up a project
21+
22+
Run this to create a new contract project:
23+
24+
```bash
25+
aztec new counter
26+
```
27+
28+
Your structure should look like this:
29+
30+
```tree
31+
.
32+
|-counter
33+
| |-src
34+
| | |-main.nr
35+
| |-Nargo.toml
36+
```
37+
38+
The file `main.nr` will soon turn into our smart contract!
39+
40+
Add the following dependencies to `Nargo.toml` under the autogenerated content:
41+
42+
```toml
43+
[dependencies]
44+
aztec = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.2.0-aztecnr-rc.2", directory="aztec" }
45+
balance_set = { git="https://github.com/AztecProtocol/aztec-nr/", tag="v4.2.0-aztecnr-rc.2", directory="balance-set" }
46+
```
47+
48+
## Define the functions
49+
50+
Go to `main.nr`, and replace the boilerplate code with this contract initialization:
51+
52+
```rust
53+
use aztec::macros::aztec;
54+
55+
#[aztec]
56+
pub contract Counter {
57+
}
58+
```
59+
60+
This defines a contract called `Counter`.
61+
62+
## Imports
63+
64+
We need to define some imports.
65+
66+
Write this inside your contract, ie inside these brackets:
67+
68+
```rust
69+
pub contract Counter {
70+
// imports go here!
71+
}
72+
```
73+
74+
```rust title="imports" showLineNumbers
75+
use aztec::{
76+
macros::{functions::{external, initializer}, storage::storage},
77+
messages::message_delivery::MessageDelivery,
78+
oracle::logging::debug_log_format,
79+
protocol::{address::AztecAddress, traits::ToField},
80+
state_vars::Owned,
81+
};
82+
use balance_set::BalanceSet;
83+
```
84+
> <sup><sub><a href="https://github.com/AztecProtocol/aztec-packages/blob/v4.2.0-aztecnr-rc.2/docs/examples/contracts/counter_contract/src/main.nr#L7-L16" target="_blank" rel="noopener noreferrer">Source code: docs/examples/contracts/counter_contract/src/main.nr#L7-L16</a></sub></sup>
85+
86+
87+
- `macros::{functions::{external, initializer}, storage::storage}`
88+
Imports the macros needed to define function types (`external`, `initializer`) and the `storage` macro for declaring contract storage structures.
89+
90+
- `messages::message_delivery::MessageDelivery`
91+
Imports `MessageDelivery` for specifying how note delivery should be handled (e.g., constrained onchain delivery).
92+
93+
- `oracle::debug_log::debug_log_format`
94+
Imports a debug logging utility for printing formatted messages during contract execution.
95+
96+
- `protocol::{address::AztecAddress, traits::ToField}`
97+
Brings in `AztecAddress` (used to identify accounts/contracts) and traits for converting values to field elements, necessary for serialization and formatting inside Aztec.
98+
99+
- `state_vars::Owned`
100+
Brings in `Owned`, a wrapper for state variables that have a single owner.
101+
102+
- `use balance_set::BalanceSet`
103+
Imports `BalanceSet` from the `balance_set` dependency, which provides functionality for managing private balances (used for our counter).
104+
105+
## Declare storage
106+
107+
Add this below the imports. It declares the storage variables for our contract. We use an `Owned` state variable wrapping a `BalanceSet` to manage private balances for each owner.
108+
109+
```rust title="storage_struct" showLineNumbers
110+
#[storage]
111+
struct Storage<Context> {
112+
counters: Owned<BalanceSet<Context>, Context>,
113+
}
114+
```
115+
> <sup><sub><a href="https://github.com/AztecProtocol/aztec-packages/blob/v4.2.0-aztecnr-rc.2/docs/examples/contracts/counter_contract/src/main.nr#L18-L23" target="_blank" rel="noopener noreferrer">Source code: docs/examples/contracts/counter_contract/src/main.nr#L18-L23</a></sub></sup>
116+
117+
118+
## Keep the counter private
119+
120+
Now we’ve got a mechanism for storing our private state, we can start using it to ensure the privacy of balances.
121+
122+
Let’s create a constructor method to run on deployment that assigns an initial count to a specified owner. This function is called `initialize`, but behaves like a constructor. It is the `#[initializer]` decorator that specifies that this function behaves like a constructor. Write this:
123+
124+
```rust title="constructor" showLineNumbers
125+
#[initializer]
126+
#[external("private")]
127+
// We can name our initializer anything we want as long as it's marked as aztec(initializer)
128+
fn initialize(headstart: u64, owner: AztecAddress) {
129+
self.storage.counters.at(owner).add(headstart as u128).deliver(
130+
MessageDelivery.ONCHAIN_CONSTRAINED,
131+
);
132+
}
133+
```
134+
> <sup><sub><a href="https://github.com/AztecProtocol/aztec-packages/blob/v4.2.0-aztecnr-rc.2/docs/examples/contracts/counter_contract/src/main.nr#L25-L34" target="_blank" rel="noopener noreferrer">Source code: docs/examples/contracts/counter_contract/src/main.nr#L25-L34</a></sub></sup>
135+
136+
137+
This function accesses the counters from storage. It adds the `headstart` value to the `owner`'s counter using `at().add()`, then calls `.deliver(MessageDelivery.ONCHAIN_CONSTRAINED)` to ensure the note is delivered onchain.
138+
139+
We have annotated this and other functions with `#[external("private")]` which are ABI macros so the compiler understands it will handle private inputs.
140+
141+
## Incrementing our counter
142+
143+
Now let's implement an `increment` function to increase the counter.
144+
145+
```rust title="increment" showLineNumbers
146+
#[external("private")]
147+
fn increment(owner: AztecAddress) {
148+
debug_log_format("Incrementing counter for owner {0}", [owner.to_field()]);
149+
self.storage.counters.at(owner).add(1).deliver(MessageDelivery.ONCHAIN_CONSTRAINED);
150+
}
151+
```
152+
> <sup><sub><a href="https://github.com/AztecProtocol/aztec-packages/blob/v4.2.0-aztecnr-rc.2/docs/examples/contracts/counter_contract/src/main.nr#L36-L42" target="_blank" rel="noopener noreferrer">Source code: docs/examples/contracts/counter_contract/src/main.nr#L36-L42</a></sub></sup>
153+
154+
155+
The `increment` function works similarly to the `initialize` function. It logs a debug message, then adds 1 to the owner's counter and delivers the note onchain.
156+
157+
## Getting a counter
158+
159+
The last thing we need to implement is a function to retrieve a counter value.
160+
161+
```rust title="get_counter" showLineNumbers
162+
#[external("utility")]
163+
unconstrained fn get_counter(owner: AztecAddress) -> pub u128 {
164+
self.storage.counters.at(owner).balance_of()
165+
}
166+
```
167+
> <sup><sub><a href="https://github.com/AztecProtocol/aztec-packages/blob/v4.2.0-aztecnr-rc.2/docs/examples/contracts/counter_contract/src/main.nr#L44-L49" target="_blank" rel="noopener noreferrer">Source code: docs/examples/contracts/counter_contract/src/main.nr#L44-L49</a></sub></sup>
168+
169+
170+
This is a `utility` function used to obtain the counter value outside of a transaction. We access the `owner`'s balance from the `counters` storage variable using `at(owner)`, then call `balance_of()` to retrieve the current count. This yields a private counter that only the owner can decrypt.
171+
172+
## Compile
173+
174+
Now we've written a simple Aztec.nr smart contract, we can compile it.
175+
176+
### Compile the smart contract
177+
178+
In the `./counter/` directory, run:
179+
180+
```bash
181+
aztec compile
182+
```
183+
184+
This command compiles your Noir contract and creates a `target` folder with a `.json` artifact inside.
185+
186+
After compiling, you can generate a TypeScript class using the `aztec codegen` command.
187+
188+
In the same directory, run this:
189+
190+
```bash
191+
aztec codegen -o src/artifacts target
192+
```
193+
194+
You can now use the artifact and/or the TS class in your Aztec.js!
195+
196+
## Next Steps
197+
198+
### Optional: Learn more about concepts mentioned here
199+
200+
- [Functions and annotations like `#[external("private")]`](../../aztec-nr/framework-description/functions/function_transforms.md#private-functions)

0 commit comments

Comments
 (0)