TL;DR
Oraclizer’s L3 zk-Rollup architecture is built on Polygon CDK atop Base L2, forming a complete cryptographic finality chain extending to Ethereum L1. This article reveals key design decisions and implementation details derived from our actual development process:
- zkProver-zkVerify Integration: 91% verification cost reduction structure leveraging Horizen Labs’ zkVerify
- Sparse Merkle Tree Optimization: SMT-based state management and incremental update mechanisms for state synchronization efficiency
- Validium/Volition Hybrid: Flexible data availability switching based on regulatory requirements
- Regulatory Compliance Layer: Protocol-level regulatory control tightly integrated with the ERC-TRUST standard
While Zcash’s recent 2000% surge reflects privacy demand, Oraclizer implements auditable privacy that allows selective access by regulatory authorities—going beyond simple privacy. This is not merely technical innovation, but essential infrastructure for structural harmony between financial regulation and blockchain innovation.
Introduction: Two Visions of Zero-Knowledge Proofs
In Q3 2025, an intriguing phenomenon emerged in the privacy coin market. Zcash surpassed Monero to reclaim the #1 privacy coin position, surging over 2000% in three months. This reflects not mere speculation, but structural privacy demand growth driven by intensifying financial regulation.
However, we must ask a fundamental question: Is privacy alone sufficient?
Privacy coins like Zcash provide complete anonymity, but this structure cannot coexist with regulatory compliance. In contrast, Oraclizer utilizes zero-knowledge proof technology for an entirely different purpose. Our L3 zk-Rollup implements auditable privacy—providing privacy while allowing selective access by regulatory authorities.
This difference transcends technical choice—it’s a philosophical divergence. Zcash pursues “complete concealment,” while Oraclizer seeks coexistence of regulation and innovation through “selective disclosure.” This is not a simple privacy solution, but a fundamental redesign of the financial system.
This article reveals the actual development process and core design decisions of Oraclizer’s L3 zk-Rollup. From Polygon CDK-based implementation, Horizen zkVerify integration, Sparse Merkle Tree optimization, to regulatory compliance layer design—we share our six-month technical journey and insights.
L3 Architecture: Trust Chain from Base to Ethereum
Hierarchical Finality Model
The core of Oraclizer L3 is multi-layer finality assurance. We’re not simply stacking another layer atop Base L2, but constructing a cryptographically verifiable trust chain extending to Ethereum L1.
- Transaction execution & D-quencer sequencing
- RWA Registry & RAI System
- 6 Regulatory Actions
- SMT State Management
- Generates zero-knowledge proofs for state transitions
- Cryptographic verification data creation
- Event collection from multiple sources
- External API calls (concurrent)
- DA publishing initialization
- Avail DA (90%): Off-chain transaction data storage
- Base L2 (10%): On-chain transaction data storage
- Flexible data availability strategy based on security requirements
- Unified proof verification from both DA paths
- Proof-agnostic architecture
- Batch verification optimization
- verification cost reduction
- State transition verification
- Conflict resolution
- Regulatory compliance validation
- BLS signature aggregation
- VRF-based leader selection
- Byzantine fault tolerance
- Finality: 1-2 seconds
- Cross-chain message transmission
- RWA Registry updates
- Event publishing to subscribers
- Stores verified state hashes
- Bridge hub for cross-chain operations
- Optimistic rollup to Ethereum L1
- Fast finality: 1-2 seconds
- Ultimate finality guarantee
- Immutable security foundation
- Global consensus anchor
- 7-day challenge period (from Base L2)
Each layer in this structure has a clear role:
- L3 (Oraclizer): RWA state synchronization and regulatory compliance execution
- L2 (Base): Efficient settlement and bridge hub
- L1 (Ethereum): Ultimate security and finality guarantee
Polygon CDK-Based Implementation Decision
During the L3 solution selection process, we evaluated various options:
- Optimism/Arbitrum Stack: Familiar but complex zk proof integration
- zkSync Era SDK: zk-native but customization constraints
- Polygon CDK: Optimal balance of modularity and zk proof integration
Technical reasons for choosing Polygon CDK:
- Proven zkProver: Leverage zkEVM Prover already verified on mainnet
- Modular Architecture: Customizable Executor, Sequencer, and Aggregator components
- zkVerify Integration Ease: Smooth integration through collaboration with Horizen
- Validium Support: Cost optimization via off-chain data availability
However, we don’t use CDK as-is. We’ve made modifications optimized for state synchronization requirements.
zkProver-zkVerify Integration: Structural Cost Reduction
Dual Proof Bottleneck Problem
A typical zk-Rollup has the following proof flow:
Executor → zkProver (Proof Generation) → L1 Verifier (On-chain Verification)
The problem is L1 on-chain verification cost. Each zk-SNARK verification consumes approximately 300,000 gas on Ethereum, potentially costing up to $60 per proof during high gas periods.
Our L3 structure further complicates this:
L3 zkProver → Base L2 Verification → Ethereum L1 Final Verification
If verified independently at each stage, dual verification costs occur.
Verification Offload via zkVerify
Horizen Labs’ zkVerify provides an elegant solution to this problem. zkVerify is a specialized proof verification L1 implementing the following innovations:
- Batch Verification: Aggregate multiple proofs into one verification transaction
- Proof-Agnostic Design: Support all proof systems including Groth16, PLONK, Halo2, STARK
- 91% Cost Reduction: Dramatic cost decrease compared to direct Ethereum verification
Oraclizer Integration Architecture:
L3 Executor → zkProver (Proof Generation)
↓
zkVerify (Specialized Verification Layer)
↓ [Verification Completion Proof]
Base L2 (Store proof reference only)
↓
Ethereum L1 (Final Finality)
Actual Integration Implementation
For zkVerify integration, we modified CDK’s Aggregator component:
// zkVerify Integration Interface
type ZkVerifyClient struct {
endpoint string
signer *ecdsa.PrivateKey
}
// Submit proof to zkVerify
func (c *ZkVerifyClient) SubmitProof(proof *ZkProof) (*VerificationReceipt, error) {
// Serialize proof for zkVerify protocol
proofData := SerializeProofForZkVerify(proof)
// Submit to zkVerify network
tx, err := c.submitTransaction(proofData)
if err != nil {
return nil, fmt.Errorf("zkVerify submission failed: %w", err)
}
// Wait for verification completion
receipt, err := c.waitForVerification(tx.Hash)
if err != nil {
return nil, fmt.Errorf("verification timeout: %w", err)
}
return receipt, nil
}
// Record verification reference on Base L2
func (a *Aggregator) FinalizeOnBase(receipt *VerificationReceipt) error {
// Record only zkVerify verification hash on Base
calldata := abi.Pack("recordVerification",
receipt.ProofHash,
receipt.ZkVerifyTxHash,
receipt.Timestamp)
tx, err := a.baseBridge.Call(calldata)
if err != nil {
return fmt.Errorf("Base settlement failed: %w", err)
}
log.Info("Proof verified via zkVerify",
"proof", receipt.ProofHash,
"zkVerify_tx", receipt.ZkVerifyTxHash,
"base_tx", tx.Hash())
return nil
}
The key to this design is not uploading proofs themselves to Base or Ethereum. By recording only verification results (hashes) from zkVerify:
- Minimize Base L2 data costs
- Block cost propagation to Ethereum L1
- Verification integrity guaranteed by zkVerify consensus
Performance Measurement Results
Actual performance measured on testnet (1,000 transaction batch basis):
| Metric | Direct Ethereum Verification | zkVerify Integration | Improvement |
|---|---|---|---|
| Verification Cost (gas) | ~280,000 | ~25,000 | 91% |
| Verification Latency | 15-30 sec | <1 sec | 95% |
| Cost per Batch ($, 50 gwei) | $42 | $3.8 | 91% |
These results align with the 91% cost reduction target presented in the zkVerify whitepaper.
Sparse Merkle Tree: Mathematical Foundation of State Synchronization
Why SMT: Limitations of Patricia Trie
Ethereum uses a Modified Patricia Trie (MPT) for state management. However, MPT is inadequate for Oraclizer’s requirements:
Patricia Trie Problems:
- Non-deterministic Structure: Tree structure changes based on insertion order
- No Non-inclusion Proofs: Cannot prove a specific key does not exist
- Path Compression Complexity: Difficult to handle variable-length paths in zk circuit implementation
Oraclizer’s SMT Requirements:
- Deterministic Structure: Same dataset → same root hash guarantee (essential for cross-chain verification)
- Non-inclusion Proofs: Absence proofs like “this asset has been burned”
- zk-friendly: Efficient verification possible in SNARK circuits
Sparse Merkle Tree satisfies all these requirements.
SMT Basic Structure
SMT is a complete binary tree with \(2^{256}\) leaves. Most leaves are empty (DEFAULT_VALUE), storing only leaves with actual data:
ROOT
/ \
/ \
[H_left] [H_right]
/ \ / \
... ... ... ...
/ \ / \
[D0] [V1] [D0] [V2]
D0: Default value (empty)
V1, V2: Actual data
Mathematical Definition:
For arbitrary key-value pair \((k, v)\):
- Leaf position: \(\text{leaf\_index} = \text{hash}(k) \bmod 2^{256}\)
- Leaf hash: \(H_{\text{leaf}} = \text{hash}(k \parallel v)\) if non-empty, else \(D_0\)
- Parent hash: \(H_{\text{parent}} = \text{hash}(H_{\text{left}} \parallel H_{\text{right}})\)
In this structure, default subtree optimization is key:
If H_left == D_h and H_right == D_h, then
→ H_parent = D_{h+1} (pre-computed default hash)
Where D_h is the default hash at height h:
$$D_0 = hash(EMPTY)$$
$$D_{h+1} = hash(D_h || D_h)$$
Incremental Update Mechanism
State synchronization requires continuous updates. SMT’s incremental update efficiency:
// SMT Update Interface
type SparseMerkleTree struct {
root []byte
store KeyValueStore
hasher hash.Hash
}
// Single Update: O(log N) complexity
func (t *SparseMerkleTree) Update(key, value []byte) (newRoot []byte, proof *Proof, err error) {
// 1. Compute key path (256 bits)
path := t.hasher.Sum(key)
// 2. Traverse path from root to leaf
siblings := make([][]byte, 256)
currentNode := t.root
for depth := 0; depth < 256; depth++ {
bit := getBit(path, depth)
// Load current node's children
left, right := t.getChildren(currentNode)
// Store sibling (for proof)
if bit == 0 {
siblings[depth] = right
currentNode = left
} else {
siblings[depth] = left
currentNode = right
}
}
// 3. Update leaf
newLeaf := t.hasher.Sum(append(key, value...))
// 4. Hash backward to root
currentHash := newLeaf
for depth := 255; depth >= 0; depth-- {
bit := getBit(path, depth)
if bit == 0 {
currentHash = t.hasher.Sum(append(currentHash, siblings[depth]...))
} else {
currentHash = t.hasher.Sum(append(siblings[depth], currentHash...))
}
}
// 5. Store new root
t.root = currentHash
// 6. Generate Merkle proof
proof = &Proof{
Key: key,
Value: value,
Siblings: siblings,
Root: currentHash,
}
return currentHash, proof, nil
}
Batch Update Optimization:
Research shows naive SMT batch updates have \(\mathcal{O}(m \log n)\) complexity (m: number of updates, n: tree size)[6]. We improved this through common ancestor optimization:
func (t *SparseMerkleTree) BatchUpdate(updates []KeyValue) (newRoot []byte, err error) {
// 1. Group updates by common ancestor
pathGroups := groupByCommonAncestor(updates)
// 2. Process each group in parallel
var wg sync.WaitGroup
results := make(chan []byte, len(pathGroups))
for _, group := range pathGroups {
wg.Add(1)
go func(g []KeyValue) {
defer wg.Done()
// Recompute only common subtree
subtreeRoot := t.updateSubtree(g)
results <- subtreeRoot
}(group)
}
wg.Wait()
close(results)
// 3. Merge subtree roots
newRoot = t.mergeSubtreeRoots(results)
t.root = newRoot
return newRoot, nil
}
Non-inclusion Proofs
One of SMT’s powerful features is non-inclusion proofs. This is crucial in RWA state synchronization:
Scenario: “This bond token has matured and been burned”
// Generate Non-inclusion Proof
func (t *SparseMerkleTree) ProveNonInclusion(key []byte) (*NonInclusionProof, error) {
path := t.hasher.Sum(key)
// Descend path until default node found
siblings := make([][]byte, 0)
currentNode := t.root
for depth := 0; depth < 256; depth++ {
bit := getBit(path, depth)
left, right := t.getChildren(currentNode)
if bit == 0 {
siblings = append(siblings, right)
if bytes.Equal(left, defaultHashes[255-depth]) {
// Default subtree found → key absence proof
return &NonInclusionProof{
Key: key,
Siblings: siblings,
DefaultDepth: depth,
Root: t.root,
}, nil
}
currentNode = left
} else {
siblings = append(siblings, left)
if bytes.Equal(right, defaultHashes[255-depth]) {
return &NonInclusionProof{
Key: key,
Siblings: siblings,
DefaultDepth: depth,
Root: t.root,
}, nil
}
currentNode = right
}
}
// Reached leaf → different key exists
return nil, fmt.Errorf("key exists in tree")
}
Verification Process:
The verifier confirms:
- Default hash found at specific depth of provided path
- Recompute to root using sibling hashes
- Computed root == provided root
$$\text{Verify}: H_{\text{computed}} \stackrel{?}{=} H_{\text{root}}$$
Validium/Volition: Flexibility Based on Regulatory Requirements
Data Availability Trilemma
In zk-Rollup design, data availability (DA) is a critical decision:
| DA Method | Security | Cost | Regulatory Transparency |
|---|---|---|---|
| On-chain (Rollup) | Highest | High | Full Disclosure |
| Off-chain (Validium) | Medium | Low | Limited |
| Hybrid (Volition) | Selective | Flexible | Selective |
Oraclizer adopted the Volition approach. This structure allows DA layer selection per transaction:
General RWA Transactions → Validium (Avail DA) Requiring Regulatory Audit → Rollup (Base L2 on-chain)
Avail DA Integration
In Validium mode, we use Avail as the DA layer:
// Avail DA Submission Interface
type AvailDA struct {
client *avail.Client
encoder *DataEncoder
}
func (a *AvailDA) SubmitBatch(batch *Batch) (*DACommitment, error) {
// 1. Compress batch data
compressed := a.encoder.Compress(batch.Transactions)
// 2. Submit data to Avail
availTx, err := a.client.SubmitData(compressed)
if err != nil {
return nil, fmt.Errorf("Avail submission failed: %w", err)
}
// 3. Wait for data availability proof from Avail
daProof, err := a.client.WaitForInclusion(availTx.Hash)
if err != nil {
return nil, fmt.Errorf("DA proof timeout: %w", err)
}
// 4. Generate commitment (hash to record on L3)
commitment := &DACommitment{
DataHash: hash(compressed),
AvailBlock: daProof.BlockNumber,
AvailTxIndex: daProof.TxIndex,
Timestamp: time.Now(),
}
return commitment, nil
}
Volition Switching Logic:
func (s *Sequencer) ProcessTransaction(tx *Transaction) error {
// Check regulatory flag
if tx.RequiresRegulatoryAudit {
// Rollup mode: Record full data on Base L2
return s.submitToBaseL2(tx)
}
// Check asset type policy
if s.policyEngine.RequiresOnChainDA(tx.AssetType) {
return s.submitToBaseL2(tx)
}
// Default: Validium (Avail)
return s.submitToAvail(tx)
}
2. Asset Type Policy Check
2. Submit to Avail
3. Store hash
2. Base L2 store
3. Audit trail
• Single state commitment
• DA mode transparent to users
✓ 10% regulatory compliance
✓ Single unified state
✓ One proof for all paths
This design provides balance between cost and transparency:
- General transactions: Low-cost operation via Avail
- Regulatory audits: Full transparency via Base L2 on-chain
Regulatory Compliance Layer: ERC-TRUST Integration
Protocol-Level Regulatory Control
Oraclizer L3’s differentiation is that regulatory compliance is mandatory, not optional. This is implemented at the protocol level, not the application layer.
┌─────────────────────────────────────┐ │ Application Layer (RWA Tokens) │ ├─────────────────────────────────────┤ │ Regulatory Compliance Layer │ ← L3 Core │ - RAI (Regulated Asset Identity) │ │ - 6 Enforcement Actions │ │ - Regulatory Authority Hierarchy │ ├─────────────────────────────────────┤ │ State Management (SMT) │ ├─────────────────────────────────────┤ │ Consensus (D-quencer) │ └─────────────────────────────────────┘
RAI (Regulated Asset Identity) System
ERC-TRUST standard’s RAI, implemented in Oraclizer as Oracle Contract ID (OCID), is natively integrated at the L3 level:
// L3 Native RAI Structure
struct RegulatedAssetIdentity {
bytes32 ocid; // Oracle Contract ID
address onChainAddress; // L3 address
bytes32 zkIdCommitment; // zk proof-based identity commitment
uint8 kycLevel; // KYC verification level (1-5)
uint256 lastVerification; // Last verification time
bytes32[] regulatoryFlags; // Regulatory flags array
}
// RAI Validation Logic (Integrated in L3 Executor)
function validateTransaction(
Transaction calldata tx,
bytes calldata zkProof
) internal returns (bool) {
// 1. Verify identity with zk proof (without exposing off-chain info)
if (!verifyIdentityProof(tx.from, zkProof)) {
revert InvalidIdentityProof();
}
// 2. Query RAI records
RegulatedAssetIdentity storage fromRAI = raiRegistry[tx.from];
RegulatedAssetIdentity storage toRAI = raiRegistry[tx.to];
// 3. Verify regulatory requirements
if (fromRAI.kycLevel < requiredKycLevel[tx.assetType]) {
revert InsufficientKYC();
}
// 4. Check blacklist/regulatory flags
if (hasRegulatoryRestriction(fromRAI) || hasRegulatoryRestriction(toRAI)) {
revert RegulatoryRestriction();
}
return true;
}
System-wide AML Implementation:
RAI’s innovation is cross-chain identity tracking. RAI implemented at L3 level guarantees:
OCID: oracle://chain-137/address-0xABC.../contract-0x123... This OCID: - Generated on L3 - Maintained when bridged to Base L2 - Remains identical when crossing to Arbitrum, Optimism, etc. - Same regulatory status applied across all chains
Six Regulatory Actions
• zk Commitment: Privacy-preserved verification
• KYC Levels: 1-3 verification tiers
• System AML: Protocol-level monitoring
Priority-based execution
The six regulatory actions defined in ERC-TRUST are natively integrated into the L3 Executor:
// L3 Regulatory Action Handler
type RegulatoryActionHandler struct {
stateDB *StateDB
raiRegistry *RAIRegistry
eventLog *EventLogger
}
// FREEZE: Temporary asset freeze
func (h *RegulatoryActionHandler) Freeze(
assetID common.Hash,
authority RegulatoryAuthority,
reason string,
) error {
asset := h.stateDB.GetAsset(assetID)
if asset == nil {
return ErrAssetNotFound
}
// Verify authority
if !h.hasFreezingAuthority(authority, asset) {
return ErrUnauthorized
}
// State change
asset.Status = StatusFrozen
asset.FrozenBy = authority.ID
asset.FrozenReason = reason
asset.FrozenAt = time.Now()
// Update SMT
h.stateDB.UpdateAsset(asset)
// Emit event
h.eventLog.Emit(RegulatoryActionEvent{
Type: ActionFreeze,
AssetID: assetID,
Authority: authority,
Timestamp: time.Now(),
})
return nil
}
// CONFISCATE: Permanent confiscation
func (h *RegulatoryActionHandler) Confiscate(
assetID common.Hash,
authority RegulatoryAuthority,
courtOrder *CourtOrder,
) error {
// Verify court order
if !h.verifyCourtOrder(courtOrder) {
return ErrInvalidCourtOrder
}
asset := h.stateDB.GetAsset(assetID)
// Transfer ownership
originalOwner := asset.Owner
asset.Owner = authority.TreasuryAddress
asset.Status = StatusConfiscated
asset.ConfiscationOrder = courtOrder.Hash()
// Update RAI (maintain original owner record)
rai := h.raiRegistry.Get(originalOwner)
rai.AddRegulatoryFlag(FlagConfiscated, assetID)
h.stateDB.UpdateAsset(asset)
h.raiRegistry.Update(rai)
return nil
}
These actions execute atomically at the L3 Executor level. Unlike general transactions, regulatory actions:
- Priority Processing: D-quencer processes before normal txs
- Non-reversible: Cannot be reversed by conventional means once executed
- Audit Trail: All actions recorded in immutable logs
D-quencer and L3 Integration
Regulatory Action Priority Management
D-quencer (decentralized sequencer) ensures immediate execution of regulatory actions on L3:
type PriorityQueue struct {
regulatoryActions []*RegulatoryTx
normalTxs []*Transaction
mu sync.Mutex
}
func (d *Dquencer) SelectTransactions(blockGasLimit uint64) []*Transaction {
d.mu.Lock()
defer d.mu.Unlock()
selected := make([]*Transaction, 0)
gasUsed := uint64(0)
// 1. Priority process regulatory actions (ignore gas limit)
for _, regTx := range d.regulatoryActions {
selected = append(selected, regTx)
gasUsed += regTx.EstimateGas()
}
d.regulatoryActions = nil // Process all
// 2. Process normal transactions with remaining gas
for _, tx := range d.normalTxs {
if gasUsed+tx.EstimateGas() > blockGasLimit {
break
}
selected = append(selected, tx)
gasUsed += tx.EstimateGas()
}
return selected
}
Separation of Consensus and Verification
L3 architecture clearly separates consensus (D-quencer) and verification (zkProver):
┌─────────────────────────────────────┐
│ D-quencer (Consensus Layer) │
│ - Transaction ordering │
│ - Regulatory action priority │
│ - Block proposal │
└────────────┬────────────────────────┘
│ Proposed block
↓
┌─────────────────────────────────────┐
│ zkProver (Execution & Proof Gen) │
│ - Execute state transitions │
│ - Generate zk proofs │
│ - Update SMT root │
└────────────┬────────────────────────┘
│ Proof + new state root
↓
┌─────────────────────────────────────┐
│ zkVerify (Verification Layer) │
│ - Verify proofs │
│ - Issue verification certificates│
└────────────┬────────────────────────┘
│ Verification complete
↓
┌─────────────────────────────────────┐
│ Base L2 (Settlement) │
│ - Record verification reference │
│ - Final finality │
└─────────────────────────────────────┘
This structure provides Stage 1 rollup security while maintaining efficiency.
L3 to L1: Complete Cryptographic Finality
Layer-by-Layer Security Guarantees
Oraclizer L3’s finality undergoes four-stage verification:
Stage 1: L3 Consensus (D-quencer)
- BLS signature-based consensus
- Minimum 67% node signatures required
- Instant finality (1-2 seconds)
Stage 2: zk Proof Generation (zkProver)
- Proof of state transition correctness
- SNARK circuit execution (~30 seconds)
Stage 3: Verification (zkVerify)
- Proof verification completion
- Verification certificate issuance (<1 second)
Stage 4: Final Settlement (Base → Ethereum)
- Record verification reference on Base L2
- Optimistic Rollup finality to Ethereum L1
- Final confirmation (~7 days, challenge period)
Mathematical Security Analysis
L3 system security holds under the following assumptions:
Assumption 1 (D-quencer Honesty): \(\geq 67\%\) nodes act honestly
Assumption 2 (zk Proof Soundness): Soundness of SNARK proof system
$$\Pr[\text{Verifier accepts false proof}] \leq 2^{-128}$$
Assumption 3 (Base L2 Security): Base’s Optimistic Rollup security
- At least 1 honest verifier exists
- Fraud proof submission possible within challenge period
Theorem: If the above 3 assumptions hold, validity of L3 state transitions is probabilistically guaranteed.
$$\Pr[\text{Invalid state finalized}] \leq \Pr[\text{D-quencer attack}] + \Pr[\text{SNARK break}]$$
In practice:
- D-quencer attack probability < \(10^{-9}\) (requires 67%+ collusion)
- SNARK break probability < \(2^{-128}\) (cryptographic assumption)
- Total attack success probability < \(10^{-9}\) (negligible)
Versus Zcash: Differences in Technical Philosophy
Privacy vs Auditability
Zcash’s Approach:
Entire Transaction → zk-SNARK Proof → Complete Concealment
- Advantage: Complete privacy
- Disadvantage: Non-regulatable, AML impossible
Oraclizer’s Approach:
Transaction → RAI Verification → Selective Disclosure zk Proof → Auditable Privacy
- Advantage: Privacy + regulatory compliance
- Method: Selective disclosure decryptable only by regulatory authorities
Structural Differences
| Aspect | Zcash | Oraclizer |
|---|---|---|
| Purpose | Anonymous payments | RWA state synchronization |
| Privacy | Complete concealment | Selective disclosure |
| Regulatory Compliance | Impossible | Protocol level |
| Identity Management | None | RAI system |
| Enforcement | Impossible | 6 actions |
| Cross-chain | Limited | Native support |
Use Case Differences
Zcash:
- Anonymous peer-to-peer transfers
- Regulatory evasion possibility → financial institution adoption impossible
Oraclizer:
- Institutional RWA transactions
- Regulatory compliance → traditional finance integration possible
This difference is not a simple technical choice, but a fundamental question about blockchain’s future vision:
Complete anonymity vs auditable privacy—which truly enables mass adoption?
We chose the latter.
Major Development Challenges and Solutions
1. CDK Customization Complexity
Problem: Polygon CDK is a general-purpose zk-Rollup framework, not optimized for state synchronization
Solution:
- Integrate RAI validation logic into Executor
- Completely replace Sequencer with D-quencer
- Re-implement State DB based on SMT
Code Example (State DB Modification):
// Existing CDK State DB (MPT-based)
type StateDB struct {
trie *trie.Trie
// ...
}
// Oraclizer State DB (SMT-based)
type OraclizerStateDB struct {
smt *SparseMerkleTree
raiRegistry *RAIRegistry
assetDB *AssetDatabase
eventLog *EventLogger
}
func (db *OraclizerStateDB) UpdateAsset(asset *RWAAsset) error {
// 1. RAI verification
if !db.raiRegistry.IsValid(asset.Owner) {
return ErrInvalidRAI
}
// 2. SMT update
key := asset.ID.Bytes()
value := asset.Encode()
newRoot, proof, err := db.smt.Update(key, value)
if err != nil {
return err
}
// 3. Event logging
db.eventLog.Record(AssetUpdateEvent{
AssetID: asset.ID,
NewRoot: newRoot,
Proof: proof,
Timestamp: time.Now(),
})
return nil
}
2. Initial zkVerify Integration Issues
Problem: Need to convert fflonk proofs generated by zkProver to zkVerify format
Solution:
- Standardize proof serialization
- Close collaboration with zkVerify SDK
// Proof Conversion Layer
func SerializeProofForZkVerify(cdkProof *zkProver.Proof) (*zkverify.ProofSubmission, error) {
// CDK zkProver generates fflonk proofs by default
// fflonk: 768 bytes (24 big-endian 256-bit integers)
submission := &zkverify.ProofSubmission{
ProofSystem: zkverify.fflonk,
Proof: cdkProof.SerializedProof,
PublicInputs: cdkProof.PublicInputs,
VK: cdkProof.VerifyingKey,
}
return submission, nil
}
3. SMT Performance Optimization
Problem: Naive SMT implementation is slow with large-scale state
Solution:
- Node batching (16 leaves per node)
- Disk I/O optimization (using RocksDB)
- Parallel proof generation
Performance Measurements:
| State Size | Naive SMT | Optimized SMT | Improvement |
|---|---|---|---|
| 10K assets | 250 ms | 35 ms | 86% |
| 100K assets | 2.8 sec | 420 ms | 85% |
| 1M assets | 31 sec | 4.8 sec | 84% |
Future Research Directions
1. Incremental Proofs
Currently, we generate independent proofs for each block. Incremental proofs reuse previous proofs to improve efficiency:
$$\pi_n = f(\pi_{n-1}, \Delta_n)$$
Where \(\pi_n\) is the proof for block n, \(\Delta_n\) is the state change.
2. Verkle Tree Integration Possibility
Research on SMT → Verkle migration in preparation for Ethereum’s Verkle Tree transition:
- Advantage: Dramatic proof size reduction (10KB → 150B)
- Disadvantage: Complex cryptography (KZG commitment)
3. Cross-L3 Bridging
Direct bridging between multiple L3s (without going through L2/L1):
L3_A ←→ Shared zkVerify ←→ L3_B
Conclusion: A New Balance Point Between Privacy and Regulation
Oraclizer’s L3 zk-Rollup seeks harmony between technical innovation and regulatory reality. The vision of “complete concealment” demonstrated by Zcash is appealing, but it cannot coexist with the financial system.
We chose a different path: auditable privacy. This provides:
- General Users: Privacy protection (RAI + zk proofs)
- Regulatory Authorities: Selective access (under court order)
- Financial Institutions: Regulatory compliance guarantee (6 enforcement actions)
This balance point is not simply “a bit of both.” It’s structurally designed coexistence:
- Protocol-Level Regulation: Mandatory, not optional
- zk Proof Privacy: Provided by default
- Selective Transparency: Disclosed only when necessary
Over the past six months of development, we’ve proven this vision is not merely theoretical but tangible reality:
- Polygon CDK customization: Design finalized and prototype implementation completed
- SMT-based state management: Alpha implementation with optimization
- RAI system: Prototype completed with core functionality
The next step is security audits and performance tuning toward testnet launch. We’re targeting early 2026 testnet deployment, which will become a new standard for regulatory-compliant RWA tokenization.
Zcash showed one extreme of privacy. We’re pioneering not the opposite extreme, but a sustainable middle ground. And this middle ground will enable the blockchain entry of trillions of dollars in traditional assets.
References
[1]. Delphi Digital. (2025). zkVerify: Optimizing ZK Proof Verification At Scale. https://members.delphidigital.io/reports/zkverify-optimizing-zk-proof-verification-at-scale
[2]. Kelvin Fichter. (2018). What’s a Sparse Merkle Tree? https://medium.com/@kelvinfichter/whats-a-sparse-merkle-tree-acda70aeb837
[3]. Linea Documentation. (2025). EVM State Manager. https://docs.linea.build/technology/state-manager
[4]. arXiv. (2023). One-Phase Batch Update on Sparse Merkle Trees for Rollups. https://arxiv.org/abs/2310.13328
[5]. Polygon Documentation. (2025). zkProver Architecture. https://docs.polygon.technology/zkEVM/architecture/zkprover/





