You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-**Prove**: NEVER call `bb` directly. Proof generation is handled for you by the PXE through the `aztec` CLI and `aztec.js`. There is no contract-development workflow that runs `bb` by hand.
31
32
-**Other nargo commands** like `aztec-nargo fmt` and `aztec-nargo doc` are fine to use directly. The Aztec installer exposes the bundled `nargo` as `aztec-nargo`; bare `nargo` resolves to your own install (if any), not the bundled one.
32
33
33
34
## Error Handling
@@ -57,7 +58,7 @@ This prevents the two most common AI mistakes: using `nargo compile`/`nargo test
57
58
58
59
### Why this matters
59
60
60
-
LLMs have extensive training data for `nargo` (the standalone Noir compiler) but limited exposure to the `aztec` CLI wrapper. Without explicit instructions, they default to `nargo compile`, which produces artifacts missing the AVM transpilation step.
61
+
LLMs have extensive training data for `nargo` (the standalone Noir compiler) and `bb` (the Barretenberg prover CLI) but limited exposure to the `aztec` CLI wrapper. Without explicit instructions, they default to `nargo compile` (which produces artifacts missing the AVM transpilation step) or reach for `bb` to generate proofs. In an Aztec project, compilation and proving both go through the `aztec` tooling.
Where this page refers to a concrete token, it assumes the [AIP-20 fungible token standard](../../standards/aip-20.md). The partial-note primitive (`UintNote` / `PartialUintNote`) is token-agnostic; AIP-20 is one standard built on top of it.
13
+
:::
14
+
15
+
## What are partial notes?
12
16
13
17
Partial notes are notes created with incomplete data, usually during private execution, which can be completed with additional information that becomes available later, usually during public execution.
14
18
@@ -18,31 +22,77 @@ Let's say, for example, we have a `UintNote`:
18
22
19
23
The `UintNote` struct itself only contains the `value` field. Additional fields including `owner`, `randomness`, and `storage_slot` are passed as parameters during note hash computation.
20
24
21
-
When creating the note locally during private execution, the `owner` and `storage_slot` are known, but the `value` potentially is not (e.g., it depends on some onchain dynamic variable). First, a **partial note** can be created during private execution that commits to the `owner` and `randomness`, and then the note is *"completed"* to create a full note by later adding the `storage_slot` and `value` fields, usually during public execution.
25
+
When creating the note locally during private execution, the `owner` and `storage_slot` are known, but the `value` potentially is not (e.g., it depends on some onchain dynamic variable). First, a **partial note** can be created during private execution that commits to the `owner` and `randomness`, and then the note is _"completed"_ to create a full note by later adding the `storage_slot` and `value` fields, usually during public execution.
Partial notes are useful when a e.g., part of the note struct is a value that depends on dynamic, public onchain data that isn't available during private execution, such as:
31
+
Partial notes are useful when part of the note struct is a value that depends on dynamic, public onchain data that isn't available during private execution, such as:
28
32
29
33
- AMM swap prices
30
34
- Current gas prices
31
35
- Time-dependent interest accrual
32
36
37
+
They are also useful as **payment endpoints**: a recipient can create a partial note ahead of time and share the commitment with prospective senders. Senders later complete the partial note to pay the recipient, with no action needed from the recipient at payment time. See [partial notes as payment endpoints](./partial_notes_as_payment_endpoints.md) for the full design.
38
+
39
+
## The completer
40
+
41
+
A partial note is finalized by a later completion step that supplies the public fields (`storage_slot` and `value`). At that point its private preimage (`owner` and `randomness`) is not re-derived or re-checked, and the partial note itself is just a `Field` that can be copied and shared freely. If anyone holding it could complete it, they could insert a note with an arbitrary, unbacked `value` into the note hash tree, or complete the note at the wrong time or with the wrong values.
42
+
43
+
To prevent this, the creator designates a **completer** at creation time. During the constrained private execution that creates the partial note, the contract records a validity commitment `H(partial_commitment, completer)` in the nullifier tree. Completion recomputes this commitment and asserts it exists, using its presence as proof that a legitimate, constrained execution created the partial note and authorized this specific completer to supply the public values and finalize it.
44
+
45
+
With AIP-20, the completer is chosen explicitly when calling `initialize_transfer_commitment`; completion (`transfer_private_to_commitment` / `transfer_public_to_commitment`) binds the completer to the caller's `msg_sender` and debits a separately authorized `from` account.
46
+
47
+
For `UintNote`, the fields split cleanly across the two phases:
|`randomness`| Partial note creation (private) | Same commitment; fresh per note, blinds the owner |
53
+
|`completer`| Partial note creation (private) | Bound in the validity commitment `H(partial_commitment, completer)`; not part of the note hash |
54
+
|`storage_slot`| Completion (public or private) | Hashed into `note_hash = H(storage_slot, partial_commitment, value)`|
55
+
|`value`| Completion (public or private) | Same hash; supplied by the completer's call |
56
+
57
+
(`storage_slot` is typically known during private execution too; it is just not bound into the note hash until completion.)
58
+
59
+
The creator fixes who gets paid (`owner`) and who may finalize (`completer`); the completer later fixes how much (`value`). Funds therefore flow from the completing side to the note's owner: in AIP-20, completion debits the authorized `from` account (the completer itself, or a payer who authorized it) and credits the `owner` chosen by the creator.
60
+
61
+
## Single-use semantics
62
+
63
+
Each partial note is intended to be completed exactly once. The protocol does not enforce this directly: completion checks that a validity commitment exists in the nullifier tree but does not consume it, so a partial note can technically be completed more than once. However, reuse is unsafe for two independent reasons:
64
+
65
+
1.**Privacy.** The completion log is tagged by `H(partial_commitment)`. Two completions of the same partial note emit logs with the same tag, which publicly links those completions as paying the same recipient.
66
+
2.**Discovery.** The recipient's Private eXecution Environment (PXE) treats the partial note as pending until the first matching completion log is found. After the first match, the pending entry is removed. A second completion against the same commitment may not be discovered by the recipient's wallet, so the funds are effectively lost.
67
+
68
+
This is why an AIP-20 commitment should be completed only once. A second `transfer_private_to_commitment` (or `transfer_public_to_commitment`) against the same commitment is not found by the recipient's log processing on the second pass, so the amount is most likely lost.
69
+
70
+
The takeaway: treat each partial note as a one-shot object. To accept multiple payments, create multiple partial notes.
71
+
72
+
## Completion in public and private contexts
73
+
74
+
`PartialUintNote` supports completion in two contexts:
75
+
76
+
-`complete` runs in a public function (AIP-20's `transfer_public_to_commitment`). The storage slot and value are emitted in a public log tagged by the partial note's commitment. Anyone observing the chain learns the amount.
77
+
-`complete_from_private` runs in a private function (AIP-20's `transfer_private_to_commitment`). The same storage slot and value are emitted in a private log with the same tag. The payload is plaintext, but it is only discoverable by a party that can derive the tag, and the tag derives from the partial note's commitment.
78
+
79
+
For private→private completion, the privacy of the amount depends on whether the partial note's commitment itself is held secret. If the commitment is published publicly (e.g., in an onchain registry), anyone can derive the tag and read the amount from the private log payload. If the commitment is shared only with prospective senders, the amount stays hidden from outside observers.
80
+
81
+
One additional protocol constraint: `complete_from_private` requires the validity commitment to be settled in a prior transaction. A partial note cannot be both created and completed in the same private transaction. The public completion path has no such restriction.
82
+
33
83
## Implementation
34
84
35
85
All notes in Aztec use the partial note format internally. This ensures that notes produce identical note hashes regardless of whether they were created as complete notes (with all fields known in private) or as partial notes (completed later in public). By having all notes follow the same two-phase hash commitment process, the protocol maintains consistency and allows notes created through different flows to behave identically.
36
86
37
-
### Note Structure Example
87
+
### Note structure example
38
88
39
89
The `UintNote` struct contains only the `value` field:
All notes in Aztec use the partial note format internally, even when all data is known during private execution. This ensures consistent note hash computation regardless of how the note was created.
73
123
@@ -80,7 +130,8 @@ When a note is created with all fields known (including `owner`, `storage_slot`,
80
130
81
131
This two-step process ensures that notes with identical field values produce identical note hashes, regardless of whether they were created as partial notes or complete notes.
82
132
83
-
84
-
## Partial Notes in Practice
133
+
## Partial notes in practice
85
134
86
135
To understand how to use partial notes in practice, [this AMM contract](https://github.com/AztecProtocol/aztec-packages/tree/#include_aztec_version/noir-projects/noir-contracts/contracts/app/amm_contract) uses partial notes to initiate and complete the swap of `token1` to `token2`. Since the exchange rate is onchain, it cannot be known ahead of time while executing in private so a full note cannot be created. Instead, a partial note is created for the `owner` swapping the tokens. This partial note is then completed during public execution once the exchange rate can be read.
136
+
137
+
For a different application of the same primitive, where the partial note represents an offer to be paid rather than a deferred DeFi settlement, see [partial notes as payment endpoints](./partial_notes_as_payment_endpoints.md).
0 commit comments