Below is a protocol-engineering comparison of Aztec and Aleo as they actually model private state today: both are hybrids, but they put the “privacy primitive” in very different places.
That difference drives almost everything else: lifecycle, wallet architecture, proving flow, composability, and privacy leakage.
Aztec’s design goal is not “private Bitcoin.” It is private smart contracts with Ethereum-like programmability and account abstraction. The protocol therefore keeps an account/public-state worldview for the execution environment, but it does not store private balances as mutable slots. Instead, private state is represented as notes committed into Merkle trees.
Why?
Mutable private storage is hard to prove privately in a shared state machine.
If every contract had arbitrary encrypted storage cells, the proving and synchronization model becomes much uglier: who can read which cell, how do clients discover their state, how do you update without leaking key paths, etc.
Notes give clean spend semantics.
A note can be committed, later spent by revealing a witness and producing a nullifier, exactly once. That maps well to private assets, claims, positions, and rights.
Aztec still wants composable contract calls.
Unlike a pure UTXO chain, Aztec wants contracts to call other contracts in a call stack, split work between private execution and public execution, and let accounts define custom authorization logic. So it keeps an account-oriented contract model around the note layer.
So Aztec is not “account model instead of notes.” It is account model for identity/auth/public state, notes for private state.
Aleo’s private state model starts from a different place: the core object is the record. A record is a private, owned data container that is consumed and recreated rather than updated in place. That is fundamentally UTXO-like.
Why Aleo chose this:
Single-use private objects simplify proving and parallelization.
Consuming existing records and creating new records is a natural fit for succinct proofs. You prove ownership/spendability of inputs and correctness of outputs. No hidden mutable slot needs to be updated in place.
Ownership is explicit at the data layer.
Each record has an owner and private contents. Wallets scan for records they can decrypt/control, much like note discovery on shielded systems.
Aleo programs are built around transition semantics.
A transition consumes inputs and emits outputs. Records make this model canonical for private execution.
Aleo is also not purely UTXO in the Bitcoin sense, because it has public state via mapping/finalize-style logic. But for private state, the record is the primary abstraction.
That distinction matters when you build higher-level apps:
A private Aztec contract typically defines a note type for some piece of state: balance note, claim note, position note, receipt note, etc.
During private execution, the contract computes a note, hashes it into a note hash/commitment, and emits it to be inserted into Aztec’s note tree. Separately, the wallet/PXE handles note discovery and encryption/logging so the intended owner can recover the note contents.
At protocol level, what settles publicly is not the plaintext note. What appears is a commitment and associated proof artifacts.
Conceptually:
This is one of the underappreciated engineering costs of note systems.
A note is only useful if the recipient can discover it. Aztec uses encrypted logs / tagging / PXE-side scanning machinery so wallets can identify notes meant for them. This is similar in spirit to how shielded systems require recipient-side scanning. It is not “the chain stores your hidden balance in a slot you can query.” The wallet must actually recover notes.
To spend a note, the prover supplies:
The note itself is not updated. Instead it is consumed and replaced by new notes representing the new state, often including change.
A spent note produces a nullifier. The nullifier is public and inserted into a nullifier set/tree to prevent double spends.
Important point: in Aztec, the nullifier is the public anti-double-spend marker. The chain does not need to know the note plaintext, only that:
This makes Aztec private state operationally UTXO-like even though the surrounding execution model is account-based.
Aleo records are more directly exposed in the programming model.
A transition can output one or more records. A record includes an owner and other fields. The owner is typically private, and the record is encrypted/committed so only the recipient can use it.
At chain level, Aleo stores enough public data to verify inclusion and later prevent double-spend, while the sensitive payload remains encrypted/private.
Wallets scan transitions/outputs to discover records they can decrypt. This is closer to shielded UTXO systems than to account-based chains. The application developer must assume record fragmentation and client-side state reconstruction as normal behavior.
Aleo transitions take records as inputs. If you spend a record, you do not mutate it. You consume it and produce new records as outputs.
Typical private transfer pattern:
{ owner: Alice, amount: 10 }{ owner: Bob, amount: 4 }{ owner: Alice, amount: 6 }Aleo prevents double-spending with a serial-number/nullifier style mechanism. In Aleo terminology, a spent record yields a serial number derived from spend authority over that record. Once that serial number is on-chain, the record cannot be spent again.
So on the lifecycle mechanics, Aztec notes and Aleo records are very similar:
The real difference is where the abstraction lives:
These are intentionally schematic, not copy-paste complete deployments. The point is to show the shape of equivalent logic.
// Schematic Aztec Noir-style code
#[note]
struct BalanceNote {
owner: AztecAddress,
amount: u128,
randomness: Field,
}
#[storage]
struct Storage {
balances: PrivateSet<BalanceNote>,
}
#[aztec(private)]
fn mint_private(to: AztecAddress, amount: u128) {
let note = BalanceNote {
owner: to,
amount,
randomness: std::rand::random(),
};
storage.balances.insert(note);
}
#[aztec(private)]
fn transfer_private(
input_note: BalanceNote,
recipient: AztecAddress,
send_amount: u128,
) {
// Ownership check is enforced by note spending/auth logic
assert(input_note.amount >= send_amount);
// Consume old note -> emits nullifier
storage.balances.remove(input_note);
let change = input_note.amount - send_amount;
let recipient_note = BalanceNote {
owner: recipient,
amount: send_amount,
randomness: std::rand::random(),
};
storage.balances.insert(recipient_note);
if change > 0 {
let change_note = BalanceNote {
owner: input_note.owner,
amount: change,
randomness: std::rand::random(),
};
storage.balances.insert(change_note);
}
}
What matters here:
// Schematic Leo-style code
program token_like.aleo {
record Balance {
owner: address,
amount: u64,
}
transition mint_private(to: address, amount: u64) -> Balance {
return Balance {
owner: to,
amount: amount,
};
}
transition transfer_private(
input: Balance,
to: address,
amount: u64
) -> (Balance, Balance) {
assert_eq(input.owner, self.caller);
assert(input.amount >= amount);
let recipient = Balance {
owner: to,
amount: amount,
};
let change = Balance {
owner: self.caller,
amount: input.amount - amount,
};
return (recipient, change);
}
}
What matters here:
These snippets implement the same transfer pattern, but the developer experience differs:
That sounds subtle, but it changes composability and state architecture substantially.
This is where the two ecosystems diverge hardest.
Aztec wants something close to DeFi-style contract composability, but private state makes synchronous composability difficult.
Shared contract world Private and public functions can coexist in one contract system. A protocol can maintain public registries, public accounting, and private user state in one architecture.
Private call stacks Private execution can compose multiple contract calls under a single proof flow.
Rich app-specific note logic Notes are not just currency outputs. They can encode rights, positions, receipts, pending actions, claims. A contract can interpret note creation/spend as a semantic event.
This is the context in which people talk about note hooks or note-driven composability: a note is not merely value; it can carry app semantics that downstream contracts or spending logic react to.
State discovery burden Every composable private protocol inherits note discovery complexity.
In practice, Aztec composability tends to work best when protocols agree on note formats, auth patterns, and proof boundaries.
Aleo’s model is cleaner at the transition level.
Program-to-program calls are explicit You call another program’s function as part of a transition graph. This is clearer than trying to infer effects from hidden note sets.
Records compose naturally as capabilities A record can represent ownership of value, a claim ticket, a permission, a position. Passing it to another program is conceptually simple: consume one record, produce another.
Good fit for object-capability design Since private state is packaged as owned records, many app patterns become “prove you own this object and transform it.”
Record fragmentation Like UTXO systems, applications must manage change records and object splitting/merging.
If you are building a private DEX, lending market, or generalized rollup-native application, Aztec’s model is usually more natural. If you are building private asset flows, ticketing, credentials, games with owned items, or capability-oriented workflows, Aleo’s record model is often cleaner.
Neither system gives “nothing leaks.” They hide contents, not existence.
Aztec’s biggest privacy caveat is that a private transaction is still a transaction in a shared sequenced system. Observers can correlate:
So Aztec hides state contents, but not all activity metadata.
Aleo’s leakage profile is more obviously UTXO-like:
It depends on the application.
A good engineering summary is:
This category is asymmetric today because Aztec’s current programmable private-contract stack is newer than its earlier production system, while Aleo’s ecosystem is centered around its VM/program model.
The clearest real production example from Aztec is zk.money and the Aztec Connect architecture. Users deposited assets into Aztec, received private notes representing balances/claims, and could interact with integrated protocols while keeping note contents hidden.
Why the note model fit:
This was not just a theoretical design; it ran in production and showed the operational realities of note systems:
On the newer programmable Aztec stack, the most natural applications are:
These use notes because app state is often “a privately owned claim” rather than “a public slot with an encrypted value.”
The most important production Aleo program is the native credits.aleo system itself. It demonstrates the record model directly:
Why the record model fits:
Aleo token and asset applications generally mirror the same pattern:
Why developers choose this:
Aleo’s strongest real-world use cases so far have clustered around:
That is exactly where a record model shines. It is less ideal for high-frequency DeFi state machines that want shared, continuously evolving private state across many protocols.
The simplistic story is “Aztec is account-based, Aleo is UTXO-like.” That is directionally true, but technically incomplete.
The more accurate statement is:
That leads to different strengths:
And different costs:
If you are engineering privacy-preserving applications, the practical distinction is this:
In Aztec, you usually ask:
“What notes does this contract own/manage, and how do private and public execution interact?”
In Aleo, you usually ask:
“What records are consumed and produced, and which public finalize state, if any, must this transition update?”
That is the real privacy-model difference. It is not just syntax. It is the difference between a private contract state machine and a private object-flow machine.
Thanks for reading this far. If “Aztec vs Aleo privacy models” connected with where you are, three concrete next steps:
The full Midnight ZK Cookbook index has 17 tutorials across Midnight, Aleo, Aztec, Noir, and risc0 plus 4 Chinese translations. Adjacent tutorials are listed by ecosystem on that page.
Bounty Radar tracks open ZK bounties across Algora, GitHub labels, Drips Wave, Code4rena, and Bountycaster. Browse the Aztec sub-feed; JSON at /aztec.json. The free tier is poll-based; the $19/mo Hobbyist tier pushes one filter to your Telegram in real time.
zk-pipeline-doctor is the free MIT-licensed CLI that scores any ZK project on tests, CI, docs, security, reproducibility, and language toolchain (supports Compact, Leo, Noir, Cairo, and 7 Rust zkVMs). Drop it into a GitHub Action with zk-doctor-action for diff-aware PR comments. The $15/mo Pro tier adds four cross-ecosystem deep detectors (circuit complexity, proving-system pitfalls, verifier soundness, multi-file consistency).
Drafted with AI assistance and reviewed by the author before publishing. See DISCLOSURE for the full process.
Three ways to support this work, pick whichever matches your situation:
Free alternative: Sponsor on GitHub · Star the repo · Share with one ZK developer who'd benefit