TL;DR
On June 14, 2026, the deprecated Aztec Connect RollupProcessorV3 contract was drained of roughly $2.19 million. The bug was not a stolen key or a reentrancy. It was a disagreement inside the rollup itself: the on-chain settlement loop only walked the number of transactions the attacker claimed were real (one), while the zero-knowledge proof committed all 32 public-input slots to the Layer 2 state. That left 31 "gap" slots that the ZK proof blessed but the Layer 1 contract never verified. The attacker forged deposits into those unverified slots, then withdrew them as genuine assets, packing seven fake mints and seven real withdrawals into a single atomic transaction. The contract had been deprecated three years earlier and its admin keys renounced, so once the flaw was live there was no pause button and no upgrade path. The stolen ETH, DAI, wstETH, LUSD, and Yearn vault tokens sat in the attacker's wallet, unlaundered, the day after. According to SlowMist's on-chain analysis, the loss was $2.19 million.
What Was Aztec Connect?
Aztec Connect was a privacy layer for Ethereum DeFi. It let users deposit assets into a shielded ZK-rollup and interact with mainnet protocols like Lido and Yearn through private, batched transactions. It was a real product with real deposits, and then it was wound down.
Aztec Labs deprecated Aztec Connect in March 2023 and halted the rollup's sequencer in March 2024. The team's response to this incident was blunt: "Aztec Connect was deprecated 3 years ago. Aztec Labs holds no admin keys or control over the system; it cannot be paused or upgraded by us." The Aztec Foundation added that the breach has "no connection to any smart contracts tied to the AZTEC ERC-20 token or the current Aztec network," per Cryptopolitan's writeup.
That posture matters, and we will come back to it. A renounced, immutable contract is a design choice that buys censorship resistance at the cost of a patch path. When the code is the only authority and the code is wrong, there is nobody to call.
The Root Cause: When the ZK Proof and the L1 Loop Disagree
A ZK-rollup settles by doing two things at once. It runs a zero-knowledge proof that the batch of transactions is valid, and it runs on-chain contract logic that moves the corresponding real money on Layer 1. Safety depends on those two paths describing the same batch. If the proof attests to one thing and the settlement contract acts on another, the gap between them is free money.
That is exactly what broke here. When RollupProcessorV3 processed a batch, it decoded 32 transaction slots. Two different pieces of code then read that decoded data with two different ideas of how many slots counted:
- The L1 settlement loop iterated only over
numRealTxs, a value the submitter controls. The attacker set it to 1. So the contract's own bookkeeping settled a single slot. - The SHA256 commitment that feeds the ZK public-input hash committed all 32
decoded_slotsto the Layer 2 state root.
The result, in SlowMist's words, is that "the ZK recognizes 32 slots, while the L1 only recognizes 1 slot." Slots 2 through 32, all 31 of them, were committed into the rollup's state by the proof but were never checked by the settlement contract. Whatever the attacker put in those slots became real L2 balance without any L1 verification that the backing assets existed.
The reason nothing caught it is the quiet part. A system like this is supposed to be defended in depth: the ZK circuit constrains what a valid batch looks like, the L1 contract re-checks settlement, and the verifier ties them together. Here the circuit constraint that should have forced numRealTxs to equal the number of committed slots was missing, and because that constraint was missing, the L1 layer had nothing to catch either. Three layers of defense failed in the same spot for the same reason. Depth does not help when every layer trusts the same unproven assumption.
How the Attack Worked
The whole exploit was one transaction. It contained 14 processRollup() calls in a deliberate pattern: seven mints followed by seven withdrawals.
Phase 1, mint (rollups 13277 to 13283). The attacker submitted seven malicious rollups, each stuffing forged deposits into the 31 unverified gap slots. Because the ZK proof committed those slots to the state root and the L1 loop never looked at them, the rollup accepted deposits that had no assets behind them. After seven rounds the attacker held a large fabricated Layer 2 balance.
Phase 2, withdraw (rollups 13284 to 13290). The attacker then withdrew that fabricated balance as real assets from the Layer 1 pool. The withdrawals SlowMist itemized include 908.987 ETH, 270,513 DAI, 167.890 wstETH, 9,273.734 LUSD, 4,873.857 yvDAI, 16.570 yvWETH, and 359.047 yvLUSD.
Bundling both phases into one atomic transaction was not incidental. It meant the whole sequence either landed or reverted together, with no window in between where a watcher could intervene and no chance of a partial rollback. The transaction burned 4,513,539 gas. As of the day after, per SlowMist, the funds remained at the attacker's externally owned account with no laundering started.
| Step | Action |
|---|---|
| 1. Craft the batch | Submit a rollup with numRealTxs = 1 but 32 populated decoded_slots, so the L1 loop settles one slot and the ZK proof commits all 32. |
| 2. Mint (x7) | Rollups 13277-13283 forge deposits into the 31 gap slots, building an unsupported L2 balance the contract never verified. |
| 3. Withdraw (x7) | Rollups 13284-13290 convert the fabricated L2 balance into real L1 assets from the pool. |
| 4. Atomic settle | All 14 processRollup() calls execute in one transaction (gas 4,513,539), so there is no intervention window and no partial revert. |
| 5. Rest | ~$2.19M in ETH, DAI, wstETH, LUSD, and Yearn tokens lands at the attacker EOA, unlaundered a day later. |
Two Aztec Contracts, Two Different Bugs, One Week
It is worth separating the numbers, because the coverage blurred them. This June 14 incident was the ZK settlement-boundary bug on RollupProcessorV3, and the on-chain figure is $2.19 million. Some outlets rounded it to $2.1M or $2.16M; those describe the same event.
A different $2.21 million figure belongs to a separate exploit on June 18, against another deprecated Aztec RollupProcessor at a different address. That one, per The Crypto Times, turned on an escapeHatch() function that lacked an onlyOwner check while the verifier accepted escape-hatch proofs without validating fund ownership. It is a distinct bug, on a distinct contract, days apart. So "Aztec got hit twice" is true, but the two drains share almost nothing at the code level except the common thread that both contracts were sunset products no one was maintaining. When you cite a number for the June 14 event, it is $2.19M, and the mechanism is the settlement-boundary bypass, not the escape hatch.
Why This Matters: Deprecated Is Not Decommissioned
The most useful lesson here is not about zero-knowledge cryptography. It is about lifecycle.
A deprecated contract that still custodies funds is a live contract with no maintainer. Aztec Connect had been off the roadmap for years. The sequencer was dark. And yet the RollupProcessorV3 pool still held millions in legacy user assets, and its verification logic still ran the moment someone submitted a batch to it. "We stopped supporting it" is an operational statement. On-chain, the code was as alive as the day it shipped.
Renouncing admin keys made that worse in this specific case. Key renunciation is often the right call. It removes the team as a point of censorship or a rug vector, which for a privacy protocol is close to the whole point. But it is a one-way door. Once the keys are gone, the contract cannot be paused, patched, or drained to safety by anyone, including the people who wrote it. Aztec Labs said as much: they could not pause or upgrade the system. The tradeoff is explicit. Immutability protects users from the team and exposes them to the team's bugs, forever.
The class of bug also generalizes past Aztec. Any system that proves one thing and settles another has this shape. Every rollup, every bridge with an off-chain attestation, every application that trusts a proof or a signature to summarize a batch of state changes, is relying on the proof and the settlement to agree about scope. The dangerous assumption is not "the proof is valid." The proof here was valid. The dangerous assumption is "a valid proof covers everything the settlement will act on." When the circuit does not constrain that the number of settled items equals the number of committed items, a valid proof can attest to a batch the contract never fully checked.
Lessons for Builders of Rollups and Bridges
1. Constrain the boundary, not just the contents. The missing check was an equality: the count the L1 loop iterates must equal the count the proof commits. Boundary and length invariants (numRealTxs == committed_slots) are exactly the properties formal verification and invariant testing exist to enforce. Prove that your settlement scope and your proof scope are the same set, not just that each element is individually valid.
2. Treat every submitter-controlled length as hostile. numRealTxs was attacker-supplied and it silently rescoped the settlement loop. Any value that controls how much of a structure gets processed, a count, an offset, an array length, a bitmap, is an attack surface. Validate it against the committed data, do not trust it to describe the committed data.
3. Sunset means migrate the funds out, not just turn off support. The single most effective control here would have been to not leave $2.19M sitting in a renounced, immutable, unmaintained pool. When you deprecate a product, plan an exit for the assets: a migration window, a sweep to a maintained contract, a public wind-down with a deadline. A contract holding user funds is never really "retired" while the balance is nonzero.
4. Weigh immutability against patchability before you renounce. Renouncing keys is a genuine security property, not a marketing checkbox. Decide it deliberately. If a contract is immutable and un-pausable, its invariants have to be provably correct before launch, because there is no second chance. Reserve full renunciation for code whose safety you can actually prove, and keep a scoped, time-locked emergency mechanism for the parts you cannot.
5. Monitor the pools you have stopped watching. Runtime monitoring could not have front-run an atomic drain in a single transaction. It is honest to say so. What it can do is alert on the deprecated pool the moment its balance moves or an anomalous processRollup() burst hits a contract that has been silent for a year, so the disclosure, the exchange freezes, and the community warnings start in minutes instead of hours. The parts of your system you have mentally decommissioned are precisely the parts no dashboard is pointed at. That is where the next one of these comes from.
Aztec Connect did what a lot of protocols quietly do. It shipped, it lost the roadmap, it kept the money, and it kept the code running with nobody at the wheel. The attacker did not break the cryptography. They found the one place where the proof and the settlement had drifted apart, and they walked $2.19M through the gap.
Frequently Asked Questions
What is Aztec Connect? Aztec Connect was a privacy-focused ZK-rollup that let Ethereum users interact with DeFi protocols through shielded, batched transactions. Aztec Labs deprecated it in March 2023 and halted its sequencer in March 2024, but its on-chain contracts and the funds they held remained live.
How was Aztec Connect exploited for $2.19M?
The RollupProcessorV3 contract's Layer 1 settlement loop only processed the number of transactions the submitter declared real (one), while the ZK proof committed all 32 decoded slots to the Layer 2 state. The attacker forged deposits into the 31 unverified slots and withdrew them as real assets, bundling seven mints and seven withdrawals into one atomic transaction.
Why couldn't Aztec Labs stop the attack? Aztec Labs had deprecated the product and renounced its admin keys, so it held no ability to pause or upgrade the contract. Immutability protected users from team control but also meant no one could patch or freeze the flaw once it was found.
Is the current Aztec network affected? No. Aztec Labs and the Aztec Foundation stated the incident is limited to the deprecated Aztec Connect contract and has no connection to the current Aztec network or the AZTEC ERC-20 token.
Was this the same as the other Aztec hack that week?
No. A separate $2.21M exploit on June 18, 2026 hit a different deprecated Aztec RollupProcessor via an escapeHatch() function missing access control. It is a distinct bug on a distinct contract. The June 14 event described here is the ZK settlement-boundary bypass, at $2.19M.
Sources / References
- SlowMist, Analysis of the $2.19 Million Asset Theft from Aztec Connect: ZK-Rollup Settlement Boundary Bypass
- Cryptopolitan, Aztec Labs draws line with deprecated Aztec Connect product after $2.1M exploit
- The Crypto Times, SlowMist Details Root Cause of $2.19M Aztec Connect Exploit
- The Crypto Times, Aztec Network's RollupProcessor Exploited for $2.21 Million (separate June 18 incident)
- KuCoin, Aztec Connect Hacked for $2.19M via ZK-Rollup Vulnerability
- NewsBTC, Deprecated Aztec Connect Contract Exploited For $2.19M, SlowMist Says
- Ethereum.org, ZK-Rollups overview



