Skip to main content

Intent

Provide on-demand, scoped visibility into confidential trades and positions via threshold-controlled viewing keys and/or ZK proofs that answer regulator questions without revealing unnecessary information.
Best for: Regulators need targeted visibility without blanket transparency. Enables least-privilege access with full audit trail.
Avoid when: Policy requires full public plaintext records with no confidentiality.

Ingredients

Standards

  • EAS (Ethereum Attestation Service): Access logging and disclosure audit trail
  • Threshold cryptography: Multi-party key management (e.g., Shamir Secret Sharing, TSS)
  • ZK predicate circuits: Answer specific queries with zero-knowledge proofs

Infrastructure

  • Threshold KMS: Distributed key management system with M-of-N threshold signature scheme
  • Policy engine: Evaluates disclosure requests against regulatory mandates and internal policies
  • Proof generation service: Computes ZK proofs for predicate queries

Off-chain Components

  • Request/approval workflow: Structured process for regulator disclosure requests
  • Encrypted audit storage: Append-only log of all disclosure events
  • Key rotation service: Periodic re-encryption of data with new keys

Protocol

1

Regulator Submits Scoped Request

Regulator specifies the scope of requested visibility:
{
  "requestor": "BaFin",
  "mandate": "MiFID II Article 26",
  "scope": {
    "account": "0x1234...",
    "isin": "XS1234567890",
    "dateRange": {
      "from": "2026-01-01",
      "to": "2026-01-31"
    }
  },
  "queryType": "view_key",  // or "zk_proof"
  "justification": "Suspicious transaction investigation"
}
Request is cryptographically signed by authorized regulator representative.
2

Policy Engine Evaluates Request

Automated policy checks verify:
  • Authorization: Is requestor an authorized regulator?
  • Mandate: Does the cited legal mandate support this request?
  • Scope: Is the requested scope reasonable and necessary?
  • Precedent: Are there similar approved requests?
async function evaluateDisclosureRequest(
  request: DisclosureRequest
): Promise<PolicyDecision> {
  // Verify requestor identity
  const isAuthorized = await identityRegistry.verify(
    request.requestor,
    RoleType.Regulator
  );
  
  if (!isAuthorized) {
    return { approved: false, reason: 'Unauthorized requestor' };
  }
  
  // Check mandate validity
  const mandateValid = await legalFramework.validateMandate(
    request.mandate,
    request.scope
  );
  
  if (!mandateValid) {
    return { approved: false, reason: 'Invalid legal mandate' };
  }
  
  // Verify scope is minimal necessary
  if (request.scope.dateRange.days > 90) {
    return { approved: false, reason: 'Scope too broad' };
  }
  
  return { approved: true };
}
3

Generate Disclosure Artifact

Depending on request type, generate either:

Option A: Time-Limited Viewing Key

For requests requiring direct data access:
// Threshold KMS reconstructs decryption key
const viewKey = await thresholdKMS.generateViewKey({
  scope: request.scope,
  expiry: Date.now() + 24 * 60 * 60 * 1000,  // 24 hours
  authorities: [
    'authority-1',  // Chief Compliance Officer
    'authority-2',  // Legal Counsel
    'authority-3'   // External Auditor
  ],
  threshold: 3  // All 3 must approve
});

// Key structure
interface ViewKey {
  keyMaterial: EncryptedBytes;  // AES-256 key wrapped to authorities
  scope: Scope;  // What data this key unlocks
  expiry: Timestamp;  // Auto-revoke time
  derivationPath: string;  // Key hierarchy path
}

Option B: ZK Proof for Predicate Query

For queries that can be answered without revealing raw data:
// Example: "Did account X trade ISIN Y in January?"
const predicate = {
  statement: 'account_traded_isin_in_period',
  inputs: {
    account: '0x1234...',
    isin: 'XS1234567890',
    dateRange: ['2026-01-01', '2026-01-31']
  }
};

// Generate ZK proof
const proof = await zkProver.prove({
  circuit: 'predicate-queries-v1',
  privateInputs: {
    trades: encryptedTradeHistory,  // Full data, not revealed
    decryptionKey: dataKey
  },
  publicInputs: predicate.inputs,
  publicOutputs: {
    answer: true,  // Yes, account did trade that ISIN
    tradeCount: 3  // Number of matching trades
    // Amounts, counterparties, prices NOT revealed
  }
});
4

Log Disclosure Event via EAS

Create immutable attestation of disclosure:
struct DisclosureAttestation {
  bytes32 requestId;           // Unique request identifier
  address requestor;           // Regulator address
  string mandateReference;     // Legal citation
  bytes32 scopeHash;           // Hash of scope parameters
  DisclosureType disclosureType; // VIEW_KEY or ZK_PROOF
  bytes32 artifactHash;        // Hash of view key or proof
  uint256 expiry;              // Key expiry timestamp
  address[] approvers;         // Internal approvers
  uint256 timestamp;
}

// Emit EAS attestation
bytes32 attestationId = eas.attest(
  DISCLOSURE_SCHEMA_UID,
  abi.encode(disclosureAttestation)
);

emit DisclosureGranted(
  requestId,
  requestor,
  attestationId
);
EAS provides:
  • On-chain immutable record
  • Public auditability of disclosure frequency
  • Proof of proper authorization process
5

Deliver to Regulator

Securely transmit disclosure artifact:
// Encrypt for regulator's public key
const encryptedArtifact = await crypto.publicKeyEncrypt(
  viewKey || proof,
  regulatorPublicKey
);

// Deliver via secure channel
await secureChannel.send({
  recipient: request.requestor,
  payload: encryptedArtifact,
  attestationId,
  instructions: {
    type: request.queryType,
    expiry: viewKey?.expiry,
    usage: 'Decrypt using your private key. Key auto-expires after 24h.'
  }
});

// Update audit log
await auditLog.append({
  event: 'disclosure_delivered',
  requestId: request.id,
  requestor: request.requestor,
  attestationId,
  timestamp: Date.now()
});
6

Auto-Revocation and Audit

Time-limited keys automatically expire:
// Background service checks expiry
setInterval(async () => {
  const expiredKeys = await disclosureDB.findExpired(Date.now());
  
  for (const key of expiredKeys) {
    // Mark as revoked
    await disclosureDB.update(key.id, { status: 'expired' });
    
    // Log revocation event
    await auditLog.append({
      event: 'view_key_expired',
      keyId: key.id,
      requestor: key.requestor,
      timestamp: Date.now()
    });
    
    // Optionally notify regulator
    await notifyKeyExpiry(key.requestor, key.id);
  }
}, 60 * 1000);  // Check every minute

Guarantees

Regulators receive only the minimum information necessary:
  • Scoped by account: Only specific addresses or identities
  • Scoped by asset: Only specific ISINs or token contracts
  • Scoped by time: Only specific date ranges
  • Scoped by question: ZK proofs answer yes/no queries without raw data
Example scoping levels:
Scope LevelRegulator SeesRegulator Does NOT See
NarrowSingle trade on 2026-01-15Other trades, account balance
ModerateAll trades in ISIN XYZ in January 2026Other assets, counterparty details
BroadAll activity for account 0x1234 in Q1 2026Other accounts, unrelated activity
Time-limited keys auto-expire:
  • No manual revocation required: Keys become invalid after expiry timestamp
  • Granular expiry periods: 1 hour to 30 days based on use case
  • Non-renewable: Expired keys cannot be extended; requires new request
  • Forward secrecy: Key rotation ensures old keys cannot decrypt new data
Every disclosure is logged immutably:
// Query all disclosures for audit
const disclosures = await eas.getAttestations({
  schema: DISCLOSURE_SCHEMA_UID,
  recipient: regulatorAddress
});

// Example audit report
{
  "total_disclosures": 47,
  "by_regulator": {
    "BaFin": 23,
    "SEC": 15,
    "FCA": 9
  },
  "by_scope_type": {
    "account": 31,
    "isin": 12,
    "date_range": 4
  },
  "avg_key_lifetime_hours": 36,
  "expired_keys": 45,
  "active_keys": 2
}
Demonstrates:
  • Compliance with proportionality principles
  • Audit frequency and scope
  • Key lifecycle management
ZK proofs answer questions without revealing raw data:Example queries answerable with ZK proofs:
  1. Range checks: “Did account X transact more than €10M in January?”
    • Proof reveals: Yes/No
    • Proof does NOT reveal: Exact amount, number of trades, counterparties
  2. Existence proofs: “Did account X trade with sanctioned entity Y?”
    • Proof reveals: Yes/No
    • Proof does NOT reveal: Trade details, amounts, dates
  3. Statistical queries: “What is the average trade size for ISIN Z?”
    • Proof reveals: Average amount
    • Proof does NOT reveal: Individual trades, traders, full distribution
  4. Compliance checks: “Are all trades compliant with MiFID II rules?”
    • Proof reveals: Yes/No, count of violations
    • Proof does NOT reveal: Which trades, which rules, trader identities

Trade-offs

Operational complexity: Managing threshold key custody, rotation, and re-encryption requires dedicated operational expertise.

Key Custody Requirements

  • Geographic distribution: Store key shares in different jurisdictions
  • Hardware security modules (HSMs): Protect key material in tamper-resistant hardware
  • Multi-signature governance: Require M-of-N authorities for key operations
  • Regular audits: External verification of key custody practices

Proof Authoring Complexity

Creating ZK circuits for predicate queries requires:
  • Cryptography expertise: Understand circuit design and proof systems
  • Testing rigor: Circuits must be audited for correctness and soundness
  • Performance tuning: Complex queries may take minutes to prove
  • User experience design: Abstract technical complexity from regulatory users
Example circuit development workflow:
1

Define Query

Work with regulators to formalize question: “Did account trade ISIN in period?”
2

Design Circuit

Translate to circuit logic: Input encrypted trades, output boolean + count.
3

Implement & Test

Write circuit in Noir/Circom, test with sample data, verify proofs.
4

Audit

External cryptographer reviews circuit for correctness and security.
5

Deploy

Make available to proof generation service, document usage.

Key Rotation Overhead

Periodic key rotation requires re-encrypting historical data:
// Quarterly key rotation example
async function rotateDataKeys() {
  // Generate new key
  const newKey = await crypto.randomBytes(32);
  
  // Re-encrypt all data from last quarter
  const records = await dataStore.getRecordsInRange(
    Date.now() - 90 * 24 * 60 * 60 * 1000,  // 90 days ago
    Date.now()
  );
  
  for (const record of records) {
    // Decrypt with old key
    const plaintext = await crypto.decrypt(record.data, oldKey);
    
    // Encrypt with new key
    const newCiphertext = await crypto.encrypt(plaintext, newKey);
    
    // Update storage
    await dataStore.update(record.id, { data: newCiphertext });
  }
  
  // Wrap new key to threshold authorities
  await thresholdKMS.wrap(newKey, authorities);
  
  // Securely delete old key
  await crypto.secureDelete(oldKey);
}
Cost: ~1-2 hours per 10,000 records with parallelization.

Example Workflow

Scenario: BaFin Market Abuse Investigation

1

Request Submission

BaFin suspects market manipulation in ISIN XYZ trading during January 2026:
{
  "requestor": "BaFin",
  "mandate": "MAR Article 23",
  "scope": {
    "isin": "XS1234567890",
    "dateRange": ["2026-01-01", "2026-01-31"]
  },
  "queryType": "view_key",
  "justification": "Investigating coordinated trading patterns"
}
2

Policy Evaluation

Automated checks:
  • ✅ BaFin is authorized regulator for German securities
  • ✅ MAR Article 23 covers market manipulation investigations
  • ✅ Date range is 31 days (within 90-day limit)
  • ✅ ISIN scope is specific (not blanket request)
Decision: Approved, requires 2-of-3 internal approvals.
3

Internal Approval

Workflow routed to:
  1. Chief Compliance Officer → Approved
  2. Legal Counsel → Approved
2-of-3 threshold met; proceeding to key generation.
4

View Key Generation

Threshold KMS assembles time-limited view key:
  • Key scope: All trades in ISIN XS1234567890 from 2026-01-01 to 2026-01-31
  • Expiry: 24 hours from issuance
  • Decrypts: Trade details (amounts, counterparties, timestamps)
  • Does NOT decrypt: Unrelated ISINs, trades outside date range
5

EAS Attestation

On-chain attestation recorded:
DisclosureAttestation({
  requestId: 0x7a3f...,
  requestor: 0xBaFi...,
  mandateReference: "MAR Article 23",
  scopeHash: keccak256("isin:XS1234567890,dates:2026-01-01:2026-01-31"),
  disclosureType: VIEW_KEY,
  artifactHash: keccak256(viewKey),
  expiry: 1735689600,
  approvers: [CCO_address, Legal_address],
  timestamp: 1735603200
})
Attestation ID: 0x3b7f8c2e...
6

Secure Delivery

View key encrypted with BaFin’s public key and delivered via secure API:
curl -X POST https://disclosure-api.example.com/deliver \
  -H "Authorization: Bearer ${BAFIN_API_KEY}" \
  -d '{
    "requestId": "0x7a3f...",
    "encryptedViewKey": "<encrypted bytes>",
    "attestationId": "0x3b7f8c2e...",
    "expiryTimestamp": 1735689600,
    "instructions": "Decrypt using your HSM private key. Key expires in 24 hours."
  }'
7

BaFin Decrypts and Analyzes

BaFin uses view key to decrypt matching trades:
// BaFin's analysis tool
const trades = await decryptWithViewKey(viewKey, scope);

// Revealed data
[
  {
    tradeId: "T-001",
    timestamp: "2026-01-15T10:23:45Z",
    isin: "XS1234567890",
    buyer: "Bank A",
    seller: "Bank B",
    amount: 5000000,  // €5M
    price: 98.5
  },
  {
    tradeId: "T-002",
    timestamp: "2026-01-15T10:24:12Z",
    isin: "XS1234567890",
    buyer: "Bank C",
    seller: "Bank B",
    amount: 3000000,  // €3M
    price: 98.4
  },
  // ... 15 more trades
]

// BaFin identifies coordinated selling pattern by Bank B
8

Auto-Revocation

24 hours later, view key expires:
  • Key material becomes invalid
  • BaFin can no longer decrypt data
  • Audit log records expiry event
  • BaFin retains decrypted analysis (allowed under MAR)

Security Considerations

Threshold authority compromise: If M authorities are compromised, attacker can reconstruct keys and decrypt all historical data.

Threshold Selection

ThresholdSecurityAvailabilityBest For
2-of-3ModerateHighFrequent disclosures, trusted authorities
3-of-5HighModerateBalance security and operations
5-of-7Very HighLowerMaximum security, infrequent disclosures

Authority Distribution

threshold_authorities:
  - id: authority-1
    role: Chief Compliance Officer
    location: Frankfurt, Germany
    hsm: Thales Luna HSM
    
  - id: authority-2
    role: Legal Counsel
    location: London, UK
    hsm: Utimaco HSM
    
  - id: authority-3
    role: External Auditor (Big 4)
    location: New York, USA
    hsm: AWS CloudHSM
    
  - id: authority-4
    role: Independent Director
    location: Singapore
    hsm: Azure Dedicated HSM
    
  - id: authority-5
    role: Chief Information Security Officer
    location: Tokyo, Japan
    hsm: Google Cloud HSM
Benefits:
  • Geographic diversity prevents single-jurisdiction seizure
  • Multiple HSM vendors reduce supply chain risk
  • Mix of internal/external authorities prevents insider threats

See Also

L2 Encrypted Off-chain Audit

How encrypted audit logs integrate with selective disclosure

L1 ZK Commitment Pool

On-chain privacy with selective disclosure

Modular Privacy Stack

Disclosure as a separate architectural layer

Hybrid Public-Private Modes

Selective privacy with disclosure mechanisms

External References

Build docs developers (and LLMs) love