Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Design ZK-Rollups for Scalable Private Asset Transfers

A developer guide for building a ZK-rollup optimized for high-throughput, private transfers of tokenized assets like real estate. Covers state transition logic, batch proof generation, and data compression techniques.
Chainscore © 2026
introduction
ARCHITECTURE GUIDE

How to Design ZK-Rollups for Scalable Private Asset Transfers

This guide explains the core architectural components and design decisions for building a zero-knowledge rollup optimized for private, scalable asset transfers.

A ZK-rollup for private assets combines the scalability of rollups with the confidentiality of zero-knowledge proofs. The system batches hundreds of private transactions off-chain, generates a single cryptographic proof (a ZK-SNARK or ZK-STARK) of their validity, and submits only this proof and minimal data to the base layer (L1). This design achieves two primary goals: data compression for scalability, reducing L1 gas costs by over 90%, and transaction privacy, where asset amounts and participant addresses are hidden from public observers while remaining verifiable.

The core state model is typically an account-based or UTXO-based system enhanced with privacy. A common approach uses the zk-SNARK-friendly Merkle tree to represent private balances. Each user holds a commitment (a hash) representing their encrypted balance inside this tree. To transfer assets, a user provides a zero-knowledge proof that demonstrates: 1) they own the input commitments, 2) the sum of inputs equals the sum of outputs (conservation of assets), and 3) the new output commitments are correctly inserted into the updated state tree, all without revealing the actual values.

Key design decisions involve selecting a proving system and a privacy model. For the proving system, ZK-SNARKs (like Groth16 or PLONK) offer small proof sizes (~200 bytes) and fast verification but require a trusted setup. ZK-STARKs are trustless and offer potentially faster prover times but generate larger proofs (~100 kB). The privacy model choice is between full anonymity sets, where all transactions are pooled, and selective disclosure, where users can reveal transaction details to specific parties via viewing keys, as implemented by protocols like Aztec.

The smart contract architecture on L1 consists of a verifier contract and a state manager contract. The verifier, often written in Solidity/Yul for gas efficiency, checks the validity of the submitted ZK proof. The state manager stores the root of the Merkle tree representing the latest private state. A sequencer (which can be permissioned or decentralized) orders off-chain transactions, generates the batch proof, and calls the L1 contracts. Users must trust the sequencer for liveness and ordering but not for fund safety, which is guaranteed by the cryptographic proof.

Developers must carefully handle data availability to allow users to exit the system. While transaction details are private, some data must be published to L1 so users can reconstruct their state proofs if needed. Solutions include posting all transaction nullifiers (to prevent double-spends) and new commitment hashes in a highly compressed format. Tools like Noir (a ZK DSL), Circom (circuit compiler), and frameworks like Scroll or zkSync's ZK Stack can accelerate development by providing modular components for proof generation and L1/L2 communication.

prerequisites
PREREQUISITES AND CORE CONCEPTS

How to Design ZK-Rollups for Scalable Private Asset Transfers

This guide outlines the foundational knowledge and architectural patterns required to design a ZK-rollup specifically for private asset transfers, focusing on scalability and confidentiality.

Designing a ZK-rollup for private transactions requires a deep understanding of three core components: the underlying blockchain's data availability, a specialized zero-knowledge proof system, and a custom state transition logic. Unlike public rollups, a privacy-focused design must ensure that transaction details—sender, receiver, and amount—are never revealed on-chain. The rollup's smart contract, or verifier, only needs to validate a succinct proof (e.g., a zk-SNARK or zk-STARK) that attests to the correct execution of a batch of private transactions. This approach separates computational verification from data disclosure, enabling scalability.

The privacy model is typically built upon commitment schemes and nullifiers. A user's balance is represented by a cryptographic commitment (like a Pedersen commitment) stored in the rollup's Merkle tree. To spend assets, the user generates a zero-knowledge proof demonstrating they own a commitment with sufficient funds and know the secret nullifier for it. Spending then publishes this nullifier to prevent double-spends, without revealing which commitment was used. Systems like zk.money (Aztec Connect) and Zcash pioneered this UTXO-based model, which forms the basis for many private rollup designs.

For scalability, the rollup operator (sequencer) batches hundreds of private transactions off-chain and generates a single validity proof for the entire batch. This proof is submitted to the mainnet contract alongside minimal public data, often just the new Merkle root and a list of spent nullifiers. The choice of proof system is critical: zk-SNARKs (e.g., Groth16, PLONK) offer small proof sizes and fast verification but require a trusted setup, while zk-STARKs are trustless but generate larger proofs. The design must balance proof generation time, verification cost, and the trust assumptions acceptable for the asset being transferred.

A practical implementation requires setting up a circuit using a framework like Circom, Halo2, or Noir. This circuit encodes the rules of your private state transitions. For example, a simple private transfer circuit would verify: 1) the input commitments exist in the tree, 2) the output commitments are correctly formed, 3) the sum of input values equals the sum of output values (conservation of assets), and 4) the prover knows the secrets for all inputs. The circuit is compiled into a proving key and a verification key, the latter being deployed within the rollup's verifier contract on L1.

Finally, the system requires a robust data availability solution for the operational data the rollup needs to function. While transaction details are private, some data must be published so users can construct proofs and the system can recover state. This often includes encrypted notes for recipients or the periodic publication of the state diff. Designs must decide whether to post this data to the L1 calldata (expensive but secure), to a separate data availability layer like Celestia or EigenDA, or to use validiums which trade off some security for lower costs. This choice fundamentally impacts the trust model and scalability ceiling of your rollup.

state-design
ARCHITECTURE

Step 1: Designing the Rollup State

The foundation of a ZK-rollup for private assets is its state model. This defines what data is tracked, how it's stored, and how privacy is maintained.

A rollup's state is a cryptographic commitment to the current status of all user accounts and assets. For a private asset system, this state must be designed to hide sensitive information like account balances and transaction amounts while remaining verifiable. The core data structure is typically a sparse Merkle tree (SMT) or a Merkle Patricia Trie, where each leaf node represents a user's account state. The root hash of this tree becomes the single, compact state commitment published on the base layer (e.g., Ethereum).

To enable privacy, the leaf data is encrypted or represented by a cryptographic commitment. Instead of storing plaintext balances, a leaf may contain a Pedersen commitment or a zero-knowledge proof-friendly hash like Poseidon. For example, an account state could be the tuple (public_key, balance_commitment, nonce). Only the user, with their private key, can generate proofs about their balance without revealing the actual amount. This model ensures the state root can be updated and verified without leaking sensitive on-chain data.

The state transition function is the rule set for updating this Merkle tree. It defines valid operations like transfer(secret_sender, secret_receiver, amount). A user must generate a ZK-SNARK or ZK-STARK proof that: 1) they know the secret inputs, 2) the transaction is valid (sufficient balance, correct signature), and 3) the new state root is computed correctly. The rollup sequencer batches these proofs and posts only the proof and the new state root to L1, compressing thousands of private actions into a single, cheap verification.

Key design choices impact scalability and privacy. Using a UTXO model (like Zcash) can enhance privacy by breaking linkability between transactions but requires managing many state elements. An account-based model (like Aztec) simplifies state management. The choice of cryptographic primitives (e.g., Elliptic curves like BN254 or BLS12-381) is critical for proof efficiency and security. Tools like Circom or Halo2 are used to define the state circuit that generates these proofs.

Finally, the design must account for data availability. For full privacy, transaction details are kept off-chain, requiring a secure data availability layer or reliance on the sequencer. Some designs, like validiums, post only state diffs and proofs to L1, while others post minimal calldata. The state model must be paired with a robust mechanism for users or watchtowers to challenge invalid state transitions, ensuring the system remains trust-minimized.

circuit-logic
CIRCUIT DESIGN

Step 2: Building the ZK Circuit Logic

This step focuses on translating the business logic for private asset transfers into a set of mathematical constraints that a zero-knowledge proof system can verify.

The core of a ZK-rollup for private transfers is the circuit, a program written in a domain-specific language like Circom or Noir. This circuit doesn't execute transactions; it defines the rules that all valid transactions must follow. For a private asset transfer, the circuit logic must enforce three critical constraints: balance consistency (the total assets are conserved), signature validity (the transaction is authorized), and nullifier integrity (preventing double-spends of the same note). The circuit takes private inputs (like the user's secret key and note details) and public inputs (like the new commitment and nullifier) and proves the computation was correct without revealing the secrets.

A typical circuit for a private transfer, inspired by protocols like zk.money (Aztec) or Tornado Cash, involves several components. The main function often verifies a Merkle proof that a commitment exists in the current state tree, proving the user owns the asset. It then computes a nullifier (e.g., hash(secret, commitment_index)) to mark the spent note. Finally, it creates a new commitment (e.g., hash(amount, new_owner_public_key, secret_salt)) for the output. The circuit's public outputs are this new commitment and the nullifier, which are posted on-chain. The zero-knowledge proof attests that all these operations were performed correctly according to the circuit's constraints.

Here is a simplified conceptual structure in pseudo-code, outlining the circuit's primary checks:

code
// Private inputs: secretKey, oldNote, amount, newOwnerPubKey
// Public inputs: newCommitment, nullifier, merkleRoot

function main(private, public) {
  // 1. Verify the user knows the secret for the old note
  isValidOwner = verifySignature(oldNote, secretKey);
  // 2. Verify the old note's commitment is in the Merkle tree
  isIncluded = verifyMerkleProof(oldNote.commitment, merkleRoot);
  // 3. Ensure the computed nullifier matches the public one
  computedNullifier = hash(secretKey, oldNote.index);
  assert(computedNullifier == public.nullifier);
  // 4. Ensure the new commitment is correctly formed
  computedCommitment = hash(amount, newOwnerPubKey, secretSalt);
  assert(computedCommitment == public.newCommitment);
  // 5. Implicitly enforce balance: input amount == output amount
}

This logic ensures the state transition is valid: an asset is destroyed (nullified) and a new one is created, with total supply unchanged.

Designing the circuit requires careful consideration of gas efficiency and proof generation time. Every constraint adds to the proving cost. Using optimized cryptographic primitives, like the Poseidon hash (common in ZK circuits for its efficiency in SNARKs), is crucial. Furthermore, the circuit must be designed to be universally verifiable, meaning the same verifying key on-chain can validate proofs from any user. After writing the circuit, it is compiled into a set of constraints (an R1CS or PLONKish arithmetization) and used to generate a proving key (for users) and a verifying key (for the rollup contract). This completes the foundational layer that enables private, provably correct transfers.

data-compression
DATA EFFICIENCY

Step 3: Implementing Data Compression

This step focuses on minimizing the on-chain data footprint of your ZK-Rollup for private asset transfers, a critical factor for cost and scalability.

Data compression is the mechanism that reduces the amount of transaction data published to the base layer (L1). For a private asset rollup, this is not just about saving gas; it's a core privacy-preserving technique. Instead of posting full transaction details, you publish only the minimal data required for state reconstruction and fraud proofs, known as calldata. This typically includes state differences, cryptographic commitments, and zero-knowledge proof verification keys, but never the plaintext sender, receiver, or amount.

The primary technique involves using commitment schemes. For each private transaction batch, your rollup contract publishes a single cryptographic hash, like a Merkle root or a Pedersen commitment, representing the new state. Accompanying this is a succinct ZK-SNARK proof (e.g., using Groth16 or PLONK) that verifies all transactions in the batch are valid according to the rollup's rules, without revealing their contents. The on-chain verifier contract checks this proof against the published commitment. Only if the proof is valid is the new state root accepted.

To implement this, you must design your circuit logic to be efficiently provable. This means optimizing operations within the ZK circuit (written in a language like Circom or Cairo) to minimize the number of constraints. Use hash functions like Poseidon or MiMC that are ZK-friendly. Structure your state tree to allow for efficient inclusion proofs. The output of your prover will be the proof and the public inputs, which are the compressed data posted on-chain.

A practical example is using a UTXO model for assets. Each transaction consumes input notes and creates output notes, each encrypted for the recipient. The compressed data posted includes: the old and new Merkle roots of the note commitment tree, a nullifier list (to prevent double-spends), and a transfer amount commitment. The ZK proof attests that for every input note spent, a valid nullifier was revealed, the output notes' total value equals the input, and all signatures are valid.

Finally, you must serialize this compressed data efficiently for L1 publication. Use RLP encoding or ABI encoding to pack the public inputs (roots, nullifiers) into the transaction calldata. The cost savings are substantial: a batch of 100 private transfers might compress from ~50 KB of raw data to under 2 KB of on-chain calldata. Tools like the Ethereum Execution Layer Specs provide standards for this data formatting, ensuring compatibility with L1 clients and indexers.

contract-architecture
SYSTEM ARCHITECTURE

Step 4: On-Chain Verifier and Bridge Contracts

This step implements the core on-chain components that secure the rollup: a verifier smart contract to validate zero-knowledge proofs and a bridge contract to manage asset deposits and withdrawals.

The on-chain verifier contract is the ultimate arbiter of state correctness for your ZK-rollup. Written in Solidity or another EVM-compatible language, its primary function is to verify a zero-knowledge proof (typically a zk-SNARK or zk-STARK) submitted by the rollup's operator. This proof cryptographically attests that a batch of private transactions was processed correctly according to the rollup's circuit logic, without revealing the transaction details. The contract contains the verification key, a public parameter generated during the trusted setup, and runs a fixed verification algorithm. A successful verification results in the contract accepting the new state root, making the batched transactions final on the base layer (e.g., Ethereum).

Alongside the verifier, you must deploy a bridge contract (or escrow contract) to handle asset custody. Users initiate private transfers by first depositing assets like ETH or ERC-20 tokens into this contract, which locks them on Layer 1. The bridge then mints a corresponding representation of the asset (e.g., a private-wrapped token) within the rollup's private state. Crucially, the bridge contract only allows withdrawals back to Layer 1 when presented with a valid Merkle proof that demonstrates the user's ownership of the funds in the latest verified state root. This mechanism, where the bridge trusts only the verifier's accepted state, is the security bedrock that prevents unauthorized withdrawals.

The interaction flow is sequential: 1) Users deposit to the bridge, 2) The operator processes private transactions off-chain, 3) The operator generates a ZK proof and new state root, 4) The operator calls the verifier contract with the proof, 5) Upon success, the verifier updates the official state root, and 6) Users can now withdraw using proofs against this new root. This design ensures that asset movement is always backed by verified state transitions. Key security considerations include ensuring the verifier contract is upgradeable in a decentralized manner to fix bugs, and implementing robust fraud-proof windows or challenge periods if using optimistic elements, though pure ZK-rollups typically have instant finality.

For development, you can use libraries like snarkjs and circom for proof generation and verification, and frameworks like Hardhat or Foundry for contract testing. A minimal verifier contract interface might include functions like verifyProof(bytes calldata _proof, uint256[] calldata _publicInputs) and updateStateRoot(bytes32 _newStateRoot). The bridge contract would manage a mapping of deposited balances and a function withdraw(bytes calldata _proof, address _recipient, uint256 _amount) that internally checks the proof against the current verified state root. Thoroughly auditing these contracts is non-negotiable, as they hold all user funds.

TECHNOLOGY OVERVIEW

Privacy Techniques for Rollups: A Comparison

A comparison of cryptographic methods for achieving privacy in ZK-Rollup designs for asset transfers.

Privacy FeatureZK-SNARKs (e.g., Groth16, Plonk)ZK-STARKsFully Homomorphic Encryption (FHE)

Cryptographic Assumption

Elliptic Curve Pairings (Trusted Setup)

Collision-Resistant Hashes (Post-Quantum)

Learning With Errors (LWE)

Proof Size

~200-300 bytes

~45-200 KB

N/A (Computation on Ciphertext)

Verification Time

< 10 ms

~10-100 ms

Varies (Decrypt & Verify)

Transaction Privacy

Full (Shielded Balances/Notes)

Full (Shielded Balances/Notes)

Full (Encrypted Balances)

Computation on Private Data

Post-Quantum Secure

Trusted Setup Required

Typical Gas Cost per Tx (Est.)

$0.50 - $2.00

$1.00 - $5.00

$5.00 - $20.00+

optimization-challenges
DESIGN AND IMPLEMENTATION

Optimization and Challenge Handling for ZK-Rollup Private Transfers

This section covers advanced techniques for optimizing the performance and security of a ZK-Rollup designed for private asset transfers, focusing on proof generation, data availability, and the critical challenge-response mechanism.

Optimizing a ZK-Rollup for private transfers requires balancing privacy, cost, and speed. The primary bottleneck is proof generation time, which directly impacts user experience for transaction finality. To mitigate this, implement recursive proof aggregation. Instead of generating a new proof for each block, the prover can aggregate multiple transaction proofs into a single, succinct proof. Libraries like plonky2 or Halo2 support this natively. For example, you can design your circuit to accept a previous proof's verification key as a public input, allowing you to recursively verify it within the new proof, compressing the entire chain's state transition into one verifiable argument.

Data availability for private transactions presents a unique challenge. While the transaction details are encrypted, the system must still publish state diffs or nullifiers to prevent double-spends. A common optimization is to use a balanced Merkle tree for the private state, reducing proof complexity from O(n) to O(log n). Store only the tree's new root and a minimal set of encrypted notes on-chain. For the data availability layer, consider EigenDA or Celestia as cost-effective alternatives to Ethereum calldata. This separates execution from consensus, allowing the rollup to post only essential data commitments, drastically reducing L1 gas fees.

The challenge-response mechanism is the core security guarantee, allowing anyone to dispute an invalid state transition. Your design must include a fault proof or validity proof system. For a ZK-Rollup, this is typically a challenge period (e.g., 7 days) where the published ZK proof can be verified. If the proof is invalid, a watcher submits a challenge transaction. Implement this with a smart contract function like challengeStateTransition(bytes32 _stateRoot, bytes calldata _proof). The contract must verify the proof on-chain using a verifier contract. If verification fails, it reverts the state root and slashes the prover's bond. Ensure the challenge logic is simple and gas-efficient to encourage participation.

To handle malicious provers, implement cryptographic economic security. The sequencer/prover must stake a substantial bond in the rollup contract. This bond is slashed if a challenge succeeds. Furthermore, design the system for fast finality alongside optimistic finality. Users can accept transactions as 'soft-final' after proof submission, with funds becoming fully secure after the challenge window expires. Use time-locked transactions or conditional withdrawals to enforce this. For developers, providing clear SDK functions like waitForFinality(txHash) or isChallengePeriodOver(stateRoot) abstracts this complexity from end-users building on your rollup.

Finally, continuous monitoring and upgradeability are crucial. Deploy a network of watchtowers—independent nodes that automatically verify every proof and submit challenges if needed. These can be run by users, exchanges, or dedicated services. Plan for circuit upgrades to fix bugs or improve efficiency by using a proxy verifier contract that points to the latest circuit logic. Always maintain a bug bounty program and conduct regular audits, as the security of private assets depends entirely on the correctness of the zero-knowledge circuits and the challenge system's resilience.

ZK-ROLLUP DESIGN

Frequently Asked Questions

Common technical questions and troubleshooting for developers building privacy-focused ZK-Rollups for asset transfers.

A standard ZK-Rollup, like zkSync or StarkNet, primarily scales transaction throughput by batching and proving execution correctness, but transaction data (sender, receiver, amount) is typically published on-chain as calldata. A privacy-focused ZK-Rollup modifies this model. It uses zero-knowledge proofs not just for validity, but also to conceal the transaction details within the proof itself. The on-chain verifier only checks the proof's validity and updated state root, without learning the underlying transfer data. This requires a specialized circuit design that enforces rules (e.g., balance conservation) on encrypted or committed inputs.

conclusion
IMPLEMENTATION PATH

Conclusion and Next Steps

This guide has outlined the core components for building a ZK-rollup for private asset transfers. The next steps involve integrating these concepts into a production-ready system.

To move from theory to implementation, begin by selecting a proving system. zk-SNARKs (like Groth16 or PLONK) offer small proof sizes and fast verification, ideal for on-chain settlement, while zk-STARKs provide quantum resistance and transparent setup. For privacy, integrate a shielded pool model using cryptographic commitments (e.g., Pedersen hashes) and nullifiers to prevent double-spends, ensuring asset balances remain confidential while maintaining public verifiability of state transitions.

Your development stack is critical. Use frameworks like StarkWare's Cairo or zkSync's ZK Stack for STARK-based systems, or Circom with SnarkJS for SNARK-based circuits. Implement the core state transition logic in your chosen language, then write the corresponding zero-knowledge circuit that generates proofs for valid transactions. The sequencer, which can be built with a client like Erigon or a custom node, must batch transactions, compute proofs, and post them to the L1.

Focus on these key security and optimization areas: Data availability must be guaranteed, typically by posting transaction calldata to Ethereum L1. Implement robust circuit constraints to prevent under/overflows and ensure logical correctness. Optimize proof generation time through techniques like recursive proofs for aggregating multiple rollup blocks. Finally, plan for decentralization by designing a permissionless prover network and a multi-party sequencer setup to avoid central points of failure.

For further learning, explore the documentation of leading ZK-rollups: zkSync Era, Starknet, and Polygon zkEVM. Study their architecture whitepapers and open-source components on GitHub. To experiment, set up a local development environment using the Starknet Foundry or Hardhat with zkSync plugins to deploy and test your own private L2 contracts and understand the end-to-end flow.