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 Implement Zero-Knowledge Proofs for Regulatory Compliance in Transfers

A technical guide for developers on building ZK circuits to prove compliance with AML, KYC, sanctions, and investor accreditation rules without exposing sensitive user data.
Chainscore © 2026
introduction
PRIVACY-PRESERVING VERIFICATION

How to Implement Zero-Knowledge Proofs for Regulatory Compliance in Transfers

A technical guide on using ZK proofs to verify transaction compliance without exposing sensitive user data, enabling privacy and regulatory adherence.

Zero-knowledge proofs (ZKPs) allow one party (the prover) to convince another (the verifier) that a statement is true without revealing any information beyond the validity of the statement itself. For financial transfers, this enables a powerful paradigm: proving compliance with regulations—such as sanctions screening, transaction limits, or accredited investor status—without disclosing the underlying private data like wallet addresses, transaction amounts, or user identities. This solves a core tension in Web3 between transparency and privacy, allowing protocols to operate globally while adhering to jurisdictional rules.

The implementation typically involves two main components: a circuit and a verification contract. The circuit, written in a domain-specific language like Circom or Noir, encodes the compliance logic. For example, a circuit could prove that a user's transaction is below a daily limit, that their funds are not from a sanctioned entity (by checking against a private Merkle tree of banned addresses), or that they hold a valid credential. The prover generates a proof from their private inputs and the circuit's public parameters. This proof is then verified on-chain by a smart contract.

Here is a simplified conceptual example in pseudocode for a circuit that proves a transfer amount is below a limit without revealing the amount:

code
// Circom-like circuit template
template ComplianceLimit() {
    signal private input amount;
    signal input limit; // Public limit
    signal output isCompliant;

    // Constraint: amount must be less than or equal to limit
    isCompliant <== amount <= limit;
}

The private amount is never revealed, but the proof demonstrates the <= relationship to the public limit. In practice, you would use a ZK-SNARK library like snarkjs with Circom to compile the circuit, generate a trusted setup, and create proofs.

For real-world deployment, integrating with existing compliance systems is key. A common pattern uses zk-Proof of Innocence, where a user proves their assets are not derived from a set of known illicit sources without revealing which assets they hold. This can be implemented by having regulators maintain a private state tree of sanctioned addresses. Users generate a proof that their input notes' nullifiers are not members of this tree's Merkle root, which the verifier contract checks. Projects like Tornado Cash and Aztec Network have pioneered similar concepts for privacy-preserving compliance.

When implementing, critical considerations include: the cost of proof generation and on-chain verification (using efficient curves like BN254 or BLS12-381), the security of the trusted setup ceremony, and the privacy guarantees of the underlying ZK scheme (SNARKs vs. STARKs). Tools like zkSync's ZK Stack, Polygon zkEVM, and Scroll provide developer environments with built-in proving systems. Always audit both the circuit logic and the cryptographic implementation, as bugs can lead to false proofs or data leakage.

The end-to-end flow for a compliant private transfer involves: 1) User inputs private data into a client-side prover, 2) Generating a ZK proof against the compliance circuit, 3) Submitting only the proof and public outputs to a blockchain, 4) A verifier smart contract validating the proof, and 5) Executing the transfer upon successful verification. This architecture enables decentralized applications to offer regulatory-compliant privacy, a necessity for institutional adoption and broader DeFi integration.

prerequisites
ZK COMPLIANCE GUIDE

Prerequisites and Setup

This guide outlines the technical foundation required to implement zero-knowledge proofs for verifying regulatory compliance in financial transfers, focusing on practical tools and developer workflows.

Implementing zero-knowledge proofs (ZKPs) for compliance requires a solid technical foundation. You will need proficiency in a systems language like Rust or C++, as most ZK frameworks are built for performance. Familiarity with cryptographic concepts such as elliptic curve pairings, hash functions, and commitment schemes is essential. A working knowledge of blockchain fundamentals, particularly Ethereum and its EVM, is also crucial for integrating proofs into smart contracts. This guide assumes you have a development environment ready with Node.js, a package manager like npm or yarn, and Git installed.

The core of your setup will be a ZK proving system. For regulatory compliance—where you need to prove statements like "this transaction is from a non-sanctioned address" or "the transfer amount is below a threshold"—Circom and SnarkJS are a powerful, accessible combination. Circom is a domain-specific language for defining arithmetic circuits, which are the computational statements you want to prove. SnarkJS is a JavaScript library that generates and verifies proofs from those circuits. Install them via npm: npm install -g circom snarkjs. For more advanced, programmable circuits, consider Noir by Aztec or Halo2 libraries, which offer different trade-offs in proof size and trust assumptions.

You will also need a trusted setup for most proving systems, which generates the proving and verification keys required for your specific circuit. This is a critical security step. For development and testing with SnarkJS, you can perform a ceremonial Powers of Tau setup or use pre-generated parameters. In production, this requires a secure multi-party ceremony. Furthermore, plan your integration point: proofs are typically generated off-chain by a client or server and then verified on-chain. You'll need a smart contract library like the Verifier.sol generated by SnarkJS or semaphore-zk for specific identity proofs.

Define the compliance logic you need to prove as a precise computational statement. For example, a circuit to prove a sender is not on a sanctions list might take a private input (the sender's address), a public input (the root of a Merkle tree containing allowed addresses), and output a proof that the private address is a leaf in that tree without revealing it. Start by modeling this logic in a high-level language, then translate it into your ZK circuit language. Test the circuit logic extensively with sample inputs before generating proofs, as errors in circuit design are the most common source of bugs.

Finally, structure your project repository to separate concerns. A typical layout includes: a /circuits directory for your .circom or .nr files; a /scripts folder for compilation, setup, and proof generation scripts; a /contracts directory for the verifier and any managing logic; and a /test suite for both circuit and integration tests. Use the framework's CLI tools to compile circuits into intermediate representations and ultimately into the verifier smart contract. This modular setup is vital for maintaining and auditing a compliance system that hinges on cryptographic correctness.

key-concepts
DEVELOPER GUIDES

Core ZK Compliance Concepts

Implement zero-knowledge proofs to verify transaction compliance without exposing sensitive user data. These concepts enable privacy-preserving regulatory checks.

system-architecture
SYSTEM ARCHITECTURE AND DATA FLOW

How to Implement Zero-Knowledge Proofs for Regulatory Compliance in Transfers

A technical guide to designing a system that uses ZK proofs to validate transfers against compliance rules without exposing sensitive user data.

Implementing zero-knowledge proofs (ZKPs) for compliance involves a distinct architectural pattern. The core components are a prover (client-side), a verifier (on-chain smart contract), and a compliance circuit. The user's wallet acts as the prover, generating a ZK proof that cryptographically attests a transaction adheres to predefined rules—such as sanctions screening or transfer limits—without revealing the underlying private data like the recipient's address or the exact amount. The verifier contract only needs the proof and public inputs to confirm validity. This separation ensures privacy-by-design while enabling automated, trustless enforcement.

The data flow begins with the user constructing a transaction locally. Before signing, a ZK compliance client—often a library like circom or snarkjs—computes a witness based on the private inputs (tx details) and public parameters (the compliance rule). It runs this witness through a pre-compiled arithmetic circuit, which outputs a proof. This proof, along with necessary public signals (e.g., a rule identifier), is submitted to the blockchain. The on-chain verifier, typically a gas-efficient zk-SNARK or zk-STARK verifier contract, checks the proof's validity in a single computation, approving or rejecting the transaction.

Designing the compliance circuit is the most critical development task. Using a domain-specific language like Circom, you define constraints that represent your business logic. For example, to prove a transfer is to a non-sanctioned address without revealing the address, the circuit would take a private input recipient and a public Merkle root of a permitted list. It hashes recipient, checks its inclusion in the Merkle tree, and outputs true. The proof demonstrates the check passed, but the verifier never sees recipient. Tools like the IDEN3 circomlib offer reusable circuits for such operations.

Integration requires careful off-chain and on-chain coordination. Off-chain, you need a service to maintain and publish the public parameters of your compliance rules (like updated Merkle roots) to an IPFS or API accessible to wallets. On-chain, the verifier contract must be deployed and whitelisted by the protocol's core bridge or transfer contract. When a user initiates a transfer, the frontend SDK fetches the latest rule parameters, generates the proof, and attaches it to the transaction. The core contract calls the verifier; only upon successful verification does the transfer execute.

For developers, a practical stack might involve Circom 2.0 for circuit writing, snarkjs for proof generation, and Solidity for the verifier contract using a library like semaphore or a Plonk verifier. A reference flow: 1) Compile circuit to R1CS and setup trusted ceremony, 2) Generate proving/verification keys, 3) Deploy verifier contract with the vk, 4) Client-side uses snarkjs to create proof from transaction data, 5) Contract calls verifyProof(vk, proof, publicSignals). Always audit both the circuit logic and the cryptographic implementation, as bugs can create false compliance assurances.

This architecture shifts the compliance burden from transparent, data-heavy monitoring to cryptographic verification. It enables features like private transactions for accredited investors or cross-border payments that satisfy local regulations without exposing financial history. The key trade-off is computational cost: proof generation is client-side and can be intensive, requiring optimization. However, with layer-2 solutions like zkRollups integrating similar verification, this pattern is becoming the standard for building compliant, privacy-preserving financial systems on-chain.

circuit-design-aml
ZK PROOF DESIGN

Step 1: Designing the AML/KYC Proof Circuit

This guide details the first step in building a privacy-preserving compliance system: designing a zero-knowledge circuit that proves a user's credentials meet regulatory requirements without revealing the underlying data.

The core of a ZK-based compliance system is a circuit, a program written in a domain-specific language like Circom or Noir. This circuit defines the exact computational logic for verifying a user's eligibility. For AML/KYC, the circuit's public inputs are the user's commitment (a cryptographic hash of their data) and the compliance result (e.g., isCompliant = 1). The private inputs are the sensitive data itself: the user's name, date of birth, and jurisdiction, along with the secret used to create the commitment.

The circuit's logic encodes the compliance rules. A simple rule might be: "User must be over 18 and not from a sanctioned jurisdiction." In code, this translates to constraints. For example, using Circom syntax, you would calculate the user's age from their private date of birth and the current timestamp, then assert it is greater than 18. You would also check that their private jurisdiction code is not in a hardcoded list of sanctioned country codes within the circuit.

Crucially, the circuit also verifies that the provided private data correctly corresponds to the public commitment. This is done by reconstructing the hash (e.g., MiMC or Poseidon) inside the circuit using the private data and secret, and constraining it to equal the public input commitment. This step ensures the prover is using the data they originally committed to, preventing them from fabricating compliant credentials.

For production, the circuit design must be robust. This includes implementing range checks for dates, using non-deterministic inputs to handle complex calculations like age, and optimizing the circuit size for faster proving times. The final output is an arithmetization of your policy—a set of mathematical constraints that can be used to generate a ZK-SNARK or ZK-STARK proof, demonstrating policy adherence with cryptographic certainty.

Once designed, the circuit is compiled into a proving key and verification key. The proving key is used by the user's wallet to generate a proof, while the verification key is used by the protocol or regulator to instantly verify it. This separation is what enables the trustless model: the verifier only needs the lightweight verification key and the proof, not the sensitive data or the proving key.

circuit-design-limits
CIRCUIT LOGIC

Step 2: Designing the Transaction Limit Proof Circuit

This step defines the core zero-knowledge logic that proves a user's transaction history complies with a regulatory limit, such as a daily cap, without revealing the underlying amounts or addresses.

A zero-knowledge proof circuit is a set of constraints that define a correct computation. For a transaction limit, the circuit must verify two primary conditions: 1) the sum of all relevant past transfers within a period (e.g., 24 hours) does not exceed a predefined limit L, and 2) the new transfer amount a_new does not cause the updated total to exceed L. The prover (user) inputs secret witnesses—the list of past amounts and the new amount—while the verifier (the protocol) only needs the public limit L and the proof. The circuit's job is to cryptographically enforce that the prover's secret inputs satisfy these constraints.

We implement this using a Merkle sum tree, a critical data structure for private compliance. Each leaf is a commitment to a user's transaction, hashed together with its amount. The tree's nodes store the cumulative sum of amounts in their subtree. To prove compliance for a new transfer, the user must provide a Merkle inclusion proof for their existing transaction leaves. The circuit verifies this proof, sums the amounts from the proven leaves, adds the new amount a_new, and checks total ≤ L. This proves the user knows a set of past transactions belonging to them that, combined with the new one, respect the limit, without revealing which specific leaves were used.

Here is a simplified pseudocode outline of the circuit's main logic, illustrating the constraint system. We assume the use of a ZK-SNARK framework like Circom or Halo2. The public inputs are the Merkle root root and the limit L. The private witness inputs are the new amount a_new, an array of past amounts a_past[i], and the Merkle authentication paths for those past leaves.

code
// Public inputs
signal input root;
signal input limit_L;

// Private witness inputs
signal input a_new;
signal input a_past[10]; // Array of past amounts
signal input pathElements[10][]; // Merkle path siblings
signal input pathIndices[10];   // Merkle path directions

// Internal signals
signal total = a_new;

// For each past transaction, verify inclusion and accumulate
for (let i = 0; i < 10; i++) {
    // 1. Recompute leaf hash: H(address, a_past[i])
    signal leafHash = poseidon(userNullifier, a_past[i]);
    // 2. Verify Merkle inclusion proof against the public root
    component merkleVerifier = MerkleInclusionProof(levels);
    merkleVerifier.root <== root;
    ... // Connect pathElements and indices
    // 3. Add the amount to the running total if inclusion is valid
    total += a_past[i];
}

// Enforce the regulatory limit constraint
total <= limit_L;

Key design considerations include choosing the right cryptographic primitives. The hash function (e.g., Poseidon or MiMC) must be ZK-friendly for efficient circuit constraints. The maximum number of past transactions (10 in the example) must be fixed at circuit compilation, creating a trade-off between flexibility and proof size. Furthermore, to prevent replay attacks, each transaction leaf should include a nullifier—a unique identifier derived from a user's secret and a nonce—ensuring the same past transaction cannot be reused in multiple proofs.

Finally, this circuit must be compiled into a proving key and verification key. The proving key is used by the user's wallet to generate a ZK-SNARK proof locally. The tiny verification key is used by the smart contract or regulatory node to verify the proof in constant time, typically with a single elliptic curve pairing check. This separation enables efficient on-chain verification, a prerequisite for integrating this compliance layer into existing DeFi protocols or cross-chain bridges without excessive gas costs.

circuit-design-accredited
IMPLEMENTING THE ZK CIRCUIT

Step 3: Designing the Accredited Investor Proof

This section details the core technical implementation of a zero-knowledge proof to verify accredited investor status without revealing sensitive financial data.

The core of the system is a zk-SNARK circuit that cryptographically verifies the investor meets specific criteria. For a U.S. accredited investor, common attestations include: net worth exceeding $1 million (excluding primary residence) or an annual income over $200,000 for the last two years. The circuit's logic is a set of constraints that must be satisfied for a valid proof. Instead of submitting bank statements or tax returns, the user provides cryptographic commitments to their assets and income as private inputs, while the public threshold (e.g., 1000000) is a known constant in the circuit.

To build this, you would use a ZK framework like Circom or Noir. Here's a simplified conceptual circuit in pseudo-code that checks net worth:

code
// Private inputs (known only to prover)
signal input totalAssets;
signal input totalLiabilities;
signal input primaryResidenceValue;

// Public parameters (known to all)
signal input threshold = 1000000; // $1M threshold

// Intermediate calculations (constrained within circuit)
signal netWorthExcludingResidence = totalAssets - totalLiabilities - primaryResidenceValue;

// The core constraint: net worth must be >= threshold
netWorthExcludingResidence >= threshold;

The circuit ensures the mathematical relationship holds without revealing the actual values of totalAssets, totalLiabilities, or primaryResidenceValue.

The user's data must be attested by a trusted source before being used in the circuit. This is typically done via verifiable credentials issued by a KYC provider or accountant. The user would present credentials containing signed commitments to their financial data. The ZK proof then demonstrates two things: 1) the user possesses a valid credential from an authorized issuer, and 2) the committed data within it satisfies the accredited investor formula. This separates identity verification (done once with the issuer) from compliance proof (done per transaction).

After generating the proof using their private data, the user submits it along with the public outputs to the blockchain. A verifier smart contract, which contains the verifying key for your circuit, can check the proof in a single, gas-efficient operation. A successful verification returns true, allowing the regulatory-compliant transfer to proceed. This entire flow enables programmable compliance: the same proof can be reused across multiple protocols that accept the same standard, creating a portable 'compliance passport' for the user's on-chain activity.

integration-oracle
ZK PROOF IMPLEMENTATION

Integrating with a Compliance Oracle

This guide explains how to integrate a zero-knowledge proof system with an external compliance oracle to verify transaction legitimacy without exposing sensitive user data.

A compliance oracle acts as an external, trusted service that attests to the validity of a zero-knowledge proof against a predefined regulatory policy. Instead of embedding complex legal logic directly into your smart contract, you offload the verification to a specialized oracle network like Chainlink or API3. Your system generates a ZK proof that a transaction adheres to rules (e.g., sender is not on a sanctions list, amount is below a limit), and the oracle cryptographically attests to the proof's validity before the on-chain contract executes the transfer. This separation of concerns enhances modularity and allows you to update compliance rules without costly smart contract upgrades.

The technical workflow involves three core steps. First, your client application generates a ZK proof using a library like Circom or SnarkJS. This proof demonstrates that private inputs (e.g., user ID, transaction amount) satisfy the public compliance circuit. Second, you send this proof to the oracle's API endpoint. The oracle's node runs the verification key against the proof and public signals; if valid, it signs an attestation. Finally, your smart contract receives this signed attestation via an oracle consumer contract, verifies the oracle's signature, and only then proceeds with the fund transfer. This ensures the contract logic is gated by a verified compliance check.

Here is a simplified code example for a Solidity contract consuming a compliance attestation. The contract stores the oracle's public address, receives a signed message, and uses ecrecover to validate it before releasing funds.

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract ComplianceGate {
    address public immutable complianceOracle;

    constructor(address _oracle) {
        complianceOracle = _oracle;
    }

    function executeTransfer(
        address recipient,
        uint256 amount,
        bytes32 proofHash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        // Reconstruct the signed message hash
        bytes32 messageHash = keccak256(abi.encodePacked(recipient, amount, proofHash));
        address signer = ecrecover(messageHash, v, r, s);
        
        require(signer == complianceOracle, "Invalid oracle signature");
        require(signer != address(0), "Invalid signature");
        
        // Proceed with the compliant transfer
        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Key considerations for production systems include oracle decentralization and proof standardization. Relying on a single oracle introduces a central point of failure. Instead, use a decentralized oracle network (DON) where multiple nodes must reach consensus on the proof's validity, significantly boosting security and censorship resistance. Furthermore, standardizing the proof format and public signals (e.g., using a schema like Verifiable Credentials or a common circuit interface) ensures interoperability between different oracle providers and compliance jurisdictions. This allows your application to seamlessly switch or aggregate data from multiple compliance services.

Integrating with a compliance oracle using ZK proofs provides a powerful pattern for building privacy-preserving DeFi and on-chain systems. It enables applications to enforce necessary regulations—such as travel rule compliance or jurisdictional limits—while upholding the core Web3 tenets of user privacy and self-sovereignty. By following this architecture, developers can create systems that are both compliant by design and trust-minimized, as the final state change depends only on a cryptographically verified attestation from a decentralized network.

onchain-verification
IMPLEMENTATION

Step 5: On-Chain Verification and Proof Logging

This step details how to deploy a verifier smart contract and log proof verification results on-chain, creating an immutable, auditable record for compliance.

On-chain verification is the final, critical step that anchors your zero-knowledge proof system to the blockchain's trust layer. After a user generates a proof locally—for example, proving a transfer amount is below a regulatory threshold without revealing the exact figure—that proof must be submitted to a verifier smart contract. This contract contains the verification key and the verification logic, written in a language like Solidity or Cairo, that cryptographically checks the proof against the public inputs. A successful verification returns true and triggers a state change, such as releasing funds or logging an event. This process ensures the compliance rule is enforced autonomously and trustlessly by the network.

The core of the verifier contract is a single function, often named verifyProof, which accepts the proof (typically an array of field elements or elliptic curve points) and the public inputs as parameters. For a transfer compliance check, the public input might be a cryptographic hash of the regulatory limit. Using libraries like snarkjs for Ethereum or Starknet's native verifier for Cairo, you compile your circuit's verification key into contract code. When the verifyProof function is called, it performs elliptic curve pairings or other cryptographic operations to confirm the proof is valid for the given inputs. A failed verification will revert the transaction, preventing the non-compliant action.

Beyond simple verification, proof logging is essential for audit trails. Every call to the verifier contract should emit a structured event. This event should log the proof's unique identifier (like a hash), the public inputs, the block timestamp, the verifying address, and the result. For instance: event ComplianceProofVerified(bytes32 proofHash, uint256 regulatoryLimitHash, address user, bool verified);. These on-chain events provide regulators or auditors with a permanent, tamper-proof ledger of all compliance checks. They can query this history to verify that every transfer over a certain period adhered to the rules, without needing access to private user data.

Implementing this requires careful gas optimization, as verification can be computationally expensive. Strategies include using precompiled contracts for pairing operations on Ethereum, leveraging layer-2 solutions like zkRollups where verification is cheaper, or utilizing proof aggregation to batch multiple verifications. It's also crucial to ensure the verifier contract is upgradeable in a controlled manner to fix bugs or update parameters, but with strict access controls to prevent manipulation of the compliance logic. The deployed contract address then becomes the single source of truth for that specific regulatory rule within your application.

In practice, a complete flow for a compliant transfer would be: 1) User's client generates a zk-SNARK proof attesting amount < limit. 2) The client submits a transaction calling transferWithProof(proof, publicInputs) on your main contract. 3) Your main contract calls the verifier contract's verifyProof. 4) If verification passes, the transfer executes and a ComplianceProofVerified event is emitted. This creates a fully automated compliance layer where the rule is enforced by code and its execution is transparently recorded on-chain for all parties to verify.

TECHNICAL EVALUATION

ZK Framework Comparison for Compliance

A comparison of popular ZK frameworks for implementing compliance proofs, focusing on features critical for regulated financial transfers.

Feature / MetricCircomHalo2Noir

Auditability of Circuit Logic

Native Support for Privacy-Preserving KYC

Proof Generation Time (approx.)

< 2 sec

< 5 sec

< 1 sec

Trusted Setup Required?

Integration with On-Chain Verifiers

EVM, Solana

EVM, Cosmos

EVM, Aztec

Developer Tooling Maturity

High

Medium

Growing

Gas Cost for On-Chain Verification

$5-15

$10-25

$3-8

Support for Custom Compliance Gates

ZK PROOFS FOR COMPLIANCE

Frequently Asked Questions

Common technical questions and implementation details for developers using zero-knowledge proofs to meet regulatory requirements in asset transfers.

A ZK-SNARK (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) is a cryptographic proof system that allows a prover to convince a verifier they possess certain information without revealing the information itself. For regulatory compliance, this enables proving a transaction adheres to rules (e.g., sender is not on a sanctions list, funds are from a verified source) while keeping the underlying private data confidential.

The core components are:

  • Arithmetic Circuit: The compliance logic (e.g., balance checks, identity checks) is compiled into a set of mathematical constraints.
  • Trusted Setup: A one-time ceremony generates public parameters (proving and verification keys). For compliance, this often requires a decentralized or audited multi-party computation (MPC) for trust.
  • Proof Generation: The prover uses the proving key, private inputs (user data), and public inputs (rule parameters) to generate a short proof.
  • Verification: Any verifier can use the verification key and the proof to check the statement's truth in milliseconds, without learning the private inputs.

Protocols like zkSync, Aztec, and Mina Protocol implement variations of this for private transactions with compliance proofs.

How to Use ZK Proofs for Regulatory Compliance in Transfers | ChainScore Guides