Hook
A freshly minted zk-rollup called NexusZK raised $120 million in Series B yesterday. Its GitHub repository has 14,000 stars. Its founders appear on every major crypto podcast. The market cap of its token, NXZK, sits at $2.3 billion. I cloned the repository at 02:00 AM Stockholm time. Eight hours later, I identified a structural flaw in the proof aggregation scheme that renders the entire security model—and every dollar in that TVL—mathematically vulnerable.
Code does not lie, but it often omits the truth. NexusZK’s omission is a silent variable in the recursive SNARK circuit.
Context
NexusZK markets itself as the first “universal settlement layer” combining ZK-rollup scalability with cross-chain composability. Its whitepaper promises 10,000 TPS, sub-second finality, and trustless bridging. The architecture uses a custom proving system called “HyperPlonk++” — a variation of Plonk with linear-time prover and constant-size proofs. The team consists of former researchers from top universities and an ex-ConsenSys engineer. The hype is real. The FOMO is deafening.
But I have seen this pattern before. In 2017, I audited Parity Wallet — the market loved it, the code did not. In 2022, I watched UST’s collapse from the inside of its codebase. The script is always the same: hype builds the floor; logic clears the debris.
Core: Systematic Teardown of NexusZK’s Security Model
Let me walk through the critical failure vector. The proof system uses a recursive SNARK to aggregate batches of transactions into a single proof. That part is standard. The vulnerability lies in the “public input commitment” stage.
NexusZK’s circuit defines a set of public inputs: the previous state root, the batch hash, and a new state root. To reduce proving time, the developers implemented an optimization they call “batch-state compression.” Instead of individually hashing each account update, they use a Merkle accumulator inside the circuit. The accumulator’s depth is dynamic, determined by the number of transactions in the batch. The circuit’s constraints are generated at compile time using a parameter MAX_BATCH_SIZE = 1024.
Here is the omission: The verifier smart contract on Ethereum does not enforce that the actual batch size equals MAX_BATCH_SIZE. An attacker can submit a batch with only 2 transactions, but craft the proof using a Merkle path that claims the accumulator contains 1024 leaves, with the remaining 1022 leaves being arbitrary zero-value updates. The circuit’s constraints check that the accumulator is constructed correctly relative to the claimed batch size, but the relationship between the Merkle path depth and the actual number of executed transactions is left unconstrained.
Trust is a variable; verification is a constant. NexusZK overlooked this constant.
The exploit path:
- Attacker creates 2 transactions: one that drains a large liquidity pool (e.g., 100,000 ETH), and a second that transfers the stolen funds to a burner address.
- Attacker generates a proof with
batch_size_claimed = 1024. The circuit constrains the Merkle tree depth to log2(1024) = 10. The attacker fills the first 2 leaves with actual transactions, and the remaining 1022 leaves with transactions that havefrom = to = address(0)andamount = 0. The circuit does not require that those zero-value transactions exist on the L2 state. They are just padding. - Attacker submits this proof to the on-chain verifier. The verifier checks that the recursive proof is valid under the constraints of the circuit. The circuit’s constraints are satisfied because the accumulator construction is internally consistent with the claimed batch size. The attacker’s 2 real transactions are included and valid. The zero-value leaves are just noise.
- The verifier accepts the batch. The state root transitions to a new root that includes the drain transaction. No rollup fraud proof or challenge period can catch this because the proof is technically valid.
This is not a theoretical attack. I have run the simulation. Using a local fork of NexusZK’s proving code and the deployed verifier contract on Sepolia, I demonstrated a state root change that includes a fictional transfer of 100,000 ETH. The verifier accepted it. The transaction cost on Ethereum was 0.0023 ETH.
Why this slipped through:
The NexusZK team performed security audits. Two audits, in fact. One by a well-known firm, another by an academic lab. Both audit reports focus on the correctness of the arithmetic constraints and the zero-knowledge property. Neither audit tested the boundary condition where actual_batch_size < MAX_BATCH_SIZE. The assumption was that the sequencer would always fill batches to capacity—an operational constraint, not a cryptographic one.
Code does not lie, but humans rationalize. The auditors assumed the protocol would enforce batch sizing off-chain. The developers assumed the auditors would catch every edge case. The investors assumed both were correct.
Implications for TVL:
NexusZK has $700 million locked in its bridge contract on Ethereum. The bridge accepts state root updates from the verifier. If an attacker executes this exploit, they can mint any amount of the bridged asset on L2, then withdraw it to Ethereum by burning tokens on L2 and generating a withdrawal proof. The bridge does not limit withdrawal velocity because the zk-proof is assumed to guarantee integrity.
The attack cost is negligible: a few hundred dollars in Ethereum gas and the computational cost of generating a proof. The reward is $700 million.
Contrarian Angle: What the Bulls Got Right
Despite this critical flaw, I must acknowledge what NexusZK does correctly. Their prover is genuinely fast—I timed it at 2.3 seconds for a 1000-transaction batch on an RTX 4090. Their developer documentation is comprehensive. The team has open-sourced the entire proving stack, which allowed me to find this bug. That is more than most projects do.
Moreover, the batch-size assumption is a solvable bug. Add one constraint: assert(MerkleDepth == log2(actual_batch_count)) hardcoded into the circuit. No dynamic parameter. The fix is ten lines of code. The team can deploy an upgrade to the verifier contract within a week.
The error is not malice. It is haste. In a bull market, speed takes precedence over rigor. The bulls are correct that NexusZK has strong fundamentals—talented team, solid proving technology, clear vision. The technology is not fundamentally broken; it is improperly bounded.
But that is exactly the problem. In a market that rewards shipping over testing, every project carries hidden variables. This one happened to be found. How many others are waiting?
Takeaway: Accountability Call
I have disclosed the vulnerability to the NexusZK team privately. They acknowledged receipt and are working on a fix. I will publish the full technical details and proof-of-concept code after the fix is deployed, or after 90 days, whichever comes first.
To the investors holding NXZK: your asset is not worthless, but its current value is a function of hype, not security. When the fix comes, check the verifier contract address. Verify the upgrade. Trust is no longer an option.
To the developers: code does not lie, but it often omits the truth. The truth here is that human oversight is the most dangerous vulnerability in any system. You build in haste. An attacker audits at leisure.
The bull market will continue. More money will flow in. More vulnerabilities will be exploited. The only question is whether you will be the one dissecting the code—or the one on the other side of the exploit.
Hype builds the floor; logic clears the debris. NexusZK’s floor just got thinner.