Batch Operations
Phase 1 of the scaling roadmap introduces the BatchCommitRevealVault -- a contract that enables up to 50 commits or reveals per transaction. It deploys alongside the existing CommitRevealVault with its own CommitmentTree and NullifierRegistry, operating in parallel without modifying or replacing the base vault.
Architecture
The batch vault shares the ProofVerifier, AssetGuard, and NativeAssetHandler with the base vault. It has its own commitment tree and nullifier registry, meaning the two vaults maintain independent Merkle trees and nullifier sets.
Batch Functions
batchCommitNative
Commits up to 50 native GHOST commitments in a single transaction.
function batchCommitNative(
bytes32[] calldata commitments,
uint256[] calldata amounts,
bytes32[] calldata quantumCommitments_
) external payable
| Parameter | Description |
|---|---|
commitments | Array of Poseidon commitment hashes |
amounts | Array of aghost amounts (must sum to msg.value) |
quantumCommitments_ | Array of quantum-resistant commitments (bytes32(0) to skip) |
The function performs a single burn for the total amount, then records each commitment individually. This is more gas-efficient than N separate transactions because the burn overhead is amortized.
batchReveal
Reveals up to 50 commitments in a single transaction.
function batchReveal(
address token,
bytes[] calldata proofs,
uint256[][] calldata publicInputsArray,
bytes32[] calldata commitments,
bytes32[] calldata quantumProofs,
bytes32[] calldata changeQuantumCommitments,
bytes[] calldata policyParamsArray
) external
Each reveal within the batch has its own ZK proof. All reveals must be for the same token type. The function iterates through each reveal, performing the full verification and minting flow for each.
batchCommitWithPolicy
Commits up to 50 commitments with policies attached (combines batch commit with policy binding).
Single-Operation Compatibility
The BatchCommitRevealVault also supports single commits and reveals with the same interface as the base vault:
| Function | Batch Equivalent |
|---|---|
commitNative() | batchCommitNative() with array length 1 |
commitNativeWithPolicy() | commitNativeWithPolicy() (same interface) |
commit() | batchCommit() with array length 1 |
reveal() | batchReveal() with array length 1 |
Constants and Limits
| Constant | Value | Description |
|---|---|---|
MAX_BATCH_SIZE | 50 | Maximum commits or reveals per batch transaction |
PUBLIC_INPUT_COUNT | 8 | Public inputs per reveal (same as base vault) |
Gas Analysis
Batch operations reduce per-operation gas overhead by amortizing transaction base costs, calldata overhead, and (for commits) the burn operation:
| Operation | Single Tx | Batch of 50 | Per-Op in Batch | Savings |
|---|---|---|---|---|
| Native commit | ~120K gas | ~2.5M gas | ~50K gas | ~58% |
| Reveal | ~350K gas | ~16M gas | ~320K gas | ~8.5% |
Reveals have less savings per operation because each reveal requires its own Groth16 proof verification (~280K gas), which cannot be amortized. The savings come from amortizing transaction overhead, storage slot warmup, and the single-mint-per-batch optimization for commits.
Practical limit: At a 25 million gas block limit, a batch of 50 reveals (~16M gas) fits within a single block with room for other transactions. This is the practical safe limit -- larger batches risk exceeding the block gas limit.
Solvency Tracking
The batch vault independently tracks solvency:
mapping(address => uint256) public totalCommitted; // Per-token committed amounts
uint256 private _totalNativeCommitted; // Native GHOST committed
The solvency invariant (totalRevealed <= totalCommitted) is enforced per token, mirroring the base vault's tracking.
No Cooldown
Unlike the base CommitRevealVault, the batch vault has no per-address cooldown between commits. This design choice reflects its intended use: batch callers are typically sequencers, agents, or high-throughput applications -- not individual end users. Rate limiting is handled at the application layer rather than the contract layer.
Use Cases
High-Throughput Token Operations
An exchange or payment processor needs to process many private transfers per block. Instead of submitting 50 separate transactions, a single batchCommitNative() call commits all 50 amounts in one transaction.
Batch Credential Issuance
A university graduates 500 students and wants to commit 500 credential hashes. Using batch operations, this requires 10 transactions instead of 500, reducing the issuance time from minutes to seconds.
Bulk Data Sealing
A data archive service commits hashes of thousands of documents per day. Batch commits make this economically viable by reducing the per-document gas cost.
Airdrop Distribution
A project wants to privately distribute tokens to 1,000 recipients. Batch commits create all 1,000 commitments in 20 transactions, with each recipient later revealing their allocation individually.
Parallel Operation
The batch vault operates in parallel with the base vault. Both are active simultaneously, and users can choose which to use based on their needs:
| Scenario | Recommended Vault |
|---|---|
| Single private transfer | Base CommitRevealVault |
| Multiple transfers in one session | BatchCommitRevealVault |
| Policy-bound single commitment | Base CommitRevealVault |
| Bulk issuance / batch processing | BatchCommitRevealVault |
Commitments made in the batch vault can only be revealed through the batch vault (and vice versa), because each vault has its own Merkle tree and nullifier registry. The two systems are independent.