<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ameer Abdulaleem</title>
    <description>The latest articles on DEV Community by Ameer Abdulaleem (@ameer_0699).</description>
    <link>https://dev.to/ameer_0699</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3849973%2Ffb074fd5-b17c-4584-86e8-8deae996da99.jpg</url>
      <title>DEV Community: Ameer Abdulaleem</title>
      <link>https://dev.to/ameer_0699</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ameer_0699"/>
    <language>en</language>
    <item>
      <title>Contract-State Accounting vs UTXO Tokens: Two Models for On-Chain Value</title>
      <dc:creator>Ameer Abdulaleem</dc:creator>
      <pubDate>Sun, 03 May 2026 09:49:13 +0000</pubDate>
      <link>https://dev.to/ameer_0699/contract-state-accounting-vs-utxo-tokens-two-models-for-on-chain-value-3m35</link>
      <guid>https://dev.to/ameer_0699/contract-state-accounting-vs-utxo-tokens-two-models-for-on-chain-value-3m35</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lr5mctwcr20t9l7s4xh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lr5mctwcr20t9l7s4xh.jpg" alt="Contract-State Accounting vs UTXO Tokens" width="800" height="336"&gt;&lt;/a&gt;## INTRODUCTION&lt;/p&gt;

&lt;p&gt;Every blockchain application that handles value needs to answer the same question: how do you track who owns what? There are two dominant approaches, and choosing between them shapes your entire contract architecture.&lt;/p&gt;

&lt;p&gt;Contract-state accounting behaves like a bank ledger. A single smart contract holds a balance map, and transactions update entries in place. The UTXO model behaves like physical cash. Each unit of value exists as an independent object that gets consumed and recreated with every transfer.&lt;/p&gt;

&lt;p&gt;Midnight supports both patterns, and each has different tradeoffs for privacy, complexity, and scalability. This tutorial explains both models, shows you how to implement them in Compact, and helps you decide which one fits your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you continue, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Compact toolchain installed by following the &lt;a href="https://docs.midnight.network/getting-started/installation" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A working understanding of Compact syntax from the &lt;a href="https://docs.midnight.network/getting-started/hello-world" rel="noopener noreferrer"&gt;Hello World tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Familiarity with the &lt;code&gt;disclose()&lt;/code&gt; function and selective disclosure concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is Contract-State Accounting?
&lt;/h2&gt;

&lt;p&gt;Contract-state accounting uses a central registry. The smart contract maintains a single data structure, typically a &lt;code&gt;Map&lt;/code&gt;, that tracks every account balance. When Alice sends 50 tokens to Bob, the contract decreases Alice's balance by 50 and increases Bob's balance by 50. The total supply is implicit in the sum of all balances.&lt;/p&gt;

&lt;p&gt;This model mirrors how a bank operates. The bank keeps a ledger, and every transaction is a double entry update to that ledger.&lt;/p&gt;

&lt;p&gt;Here is a basic implementation in Compact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export ledger balances: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Uint&amp;lt;64&amp;gt;&amp;gt;;
export ledger totalSupply: Uint&amp;lt;64&amp;gt;;

witness localSk(): Bytes&amp;lt;32&amp;gt;;

constructor(initialSupply: Uint&amp;lt;64&amp;gt;) {
    const _sk = localSk();
    let creator = getDappPublicKey(_sk);
    balances.insert(disclose(creator), disclose(initialSupply));
    totalSupply = disclose(initialSupply);
}

export circuit transfer(
    recipient: Bytes&amp;lt;32&amp;gt;,
    amount: Uint&amp;lt;64&amp;gt;
): [] {
    const _sk = localSk();
    let sender = getDappPublicKey(_sk);

    let senderBalance = balances.lookup(sender);
    assert(senderBalance &amp;gt;= amount, "Insufficient balance");

    let newSenderBalance = senderBalance - amount;
    balances.insert(sender, newSenderBalance);

    let recipientBalance = balances.lookup(recipient);
    let newRecipientBalance = recipientBalance + amount;
    balances.insert(recipient, newRecipientBalance);
}

export circuit getDappPublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "accounting:pk:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation is straightforward. Every transfer reads two balances, performs arithmetic, and writes two updated balances. The state changes are destructive. Once Alice's balance is updated, the previous balance is gone forever.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of Contract-State Accounting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Simplicity:&lt;/strong&gt; The mental model is intuitive. Developers coming from traditional backend systems immediately understand a balance map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gas predictability:&lt;/strong&gt; Each transfer performs a constant number of operations regardless of transaction history. Two map lookups, two map insertions, and some arithmetic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easy aggregation:&lt;/strong&gt; If you need to know the total supply or the number of holders, the data is right there in the contract state. You can query it directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Familiar tooling:&lt;/strong&gt; The pattern works well with standard wallet interfaces. Users see a balance and a send button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disadvantages of Contract-State Accounting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Privacy challenges:&lt;/strong&gt; In a fully public implementation, every balance is visible to every observer. Midnight mitigates this with ZK proofs, but the pattern still requires careful selective disclosure design to protect individual balances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State bloat:&lt;/strong&gt; The balance map grows with every new holder and never shrinks. Even accounts with zero balance may need to remain to preserve historical data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency issues:&lt;/strong&gt; If two transactions try to update the same balance simultaneously, one will fail. This creates friction in high throughput applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the UTXO Model?
&lt;/h2&gt;

&lt;p&gt;UTXO stands for Unspent Transaction Output. The mental model is physical cash. When you hold a ten dollar bill, that bill is a discrete object with its own serial number and value. When you pay for something that costs seven dollars using that ten dollar bill, two things happen: the ten dollar bill is destroyed, a seven dollar bill goes to the merchant, and a three dollar bill comes back to you as change.&lt;/p&gt;

&lt;p&gt;In a UTXO system, every unit of value is a distinct object stored on chain. Each object has an owner and an amount. A transaction consumes input UTXOs and creates output UTXOs. The sum of input amounts must equal the sum of output amounts, preserving the total supply.&lt;/p&gt;

&lt;p&gt;Here is a Compact implementation of a basic UTXO token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export ledger utxos: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Uint&amp;lt;64&amp;gt;&amp;gt;;
export ledger utxoOwners: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;

witness localSk(): Bytes&amp;lt;32&amp;gt;;

constructor(initialSupply: Uint&amp;lt;64&amp;gt;) {
    const _sk = localSk();
    let creator = getDappPublicKey(_sk);

    let utxoId = persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "utxo:genesis:"),
        _sk
    ]);

    utxos.insert(disclose(utxoId), disclose(initialSupply));
    utxoOwners.insert(disclose(utxoId), disclose(creator));
}

export circuit transfer(
    inputUtxoId: Opaque&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;,
    outputAmount1: Uint&amp;lt;64&amp;gt;,
    outputAmount2: Uint&amp;lt;64&amp;gt;,
    recipient1: Bytes&amp;lt;32&amp;gt;,
    recipient2: Bytes&amp;lt;32&amp;gt;
): [] {
    // Verify ownership of the input UTXO
    const _sk = localSk();
    let sender = getDappPublicKey(_sk);

    let utxoId = disclose(inputUtxoId);
    assert(utxos.member(utxoId), "UTXO does not exist");
    assert(utxoOwners.lookup(utxoId) == sender, "You do not own this UTXO");

    let inputAmount = utxos.lookup(utxoId);

    // Verify amount conservation
    assert(
        inputAmount == outputAmount1 + outputAmount2,
        "Input and output amounts must match"
    );

    // Destroy the input UTXO
    utxos.remove(utxoId);
    utxoOwners.remove(utxoId);

    // Create output UTXOs
    if (outputAmount1 &amp;gt; 0) {
        let newUtxoId1 = persistentHash&amp;lt;Vector&amp;lt;3, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
            pad(32, "utxo:output:"),
            utxoId,
            pad(32, "1")
        ]);
        utxos.insert(disclose(newUtxoId1), outputAmount1);
        utxoOwners.insert(disclose(newUtxoId1), recipient1);
    }

    if (outputAmount2 &amp;gt; 0) {
        let newUtxoId2 = persistentHash&amp;lt;Vector&amp;lt;3, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
            pad(32, "utxo:output:"),
            utxoId,
            pad(32, "2")
        ]);
        utxos.insert(disclose(newUtxoId2), outputAmount2);
        utxoOwners.insert(disclose(newUtxoId2), recipient2);
    }
}

export circuit getDappPublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "utxo:pk:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This implementation captures the essence of the UTXO model. Each UTXO is a distinct entry in the &lt;code&gt;utxos&lt;/code&gt; map with a corresponding owner. A transfer destroys one UTXO and creates up to two new ones, typically one for the recipient and one for change back to the sender.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of the UTXO Model
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Natural privacy;&lt;/strong&gt; Because UTXOs are independent objects, you can use different keys for each one. An observer cannot trivially link multiple UTXOs to the same owner without additional information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallelism;&lt;/strong&gt; Transactions that consume different UTXOs do not conflict with each other. Alice sending to Bob and Charlie sending to Dave can process simultaneously because they touch different state entries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auditability;&lt;/strong&gt; The total supply is preserved by construction. Every transaction must balance, so you can verify the entire history without trusting a central authority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resistance to replay;&lt;/strong&gt; Each UTXO has a unique identifier. Once consumed, a UTXO cannot be used again because it no longer exists in the state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disadvantages of the UTXO Model
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Complexity;&lt;/strong&gt; The model is less intuitive for developers used to account-based systems. Generating change outputs, selecting which UTXOs to consume, and managing fragmented balances adds surface area for bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fragmentation;&lt;/strong&gt; Over time, a user may accumulate many small UTXOs. Spending a large amount requires combining multiple inputs, which increases transaction size and complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State growth;&lt;/strong&gt; Every transaction creates new UTXOs. Even as old ones are consumed, the total number of UTXOs in existence tends to grow, especially if users do not consolidate their holdings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing the Two Models
&lt;/h2&gt;

&lt;p&gt;Let us examine the same scenario implemented in both models to highlight the practical differences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario: Alice sends 30 tokens to Bob
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Contract-state accounting:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The contract reads Alice's balance. If she has at least 30 tokens, it subtracts 30 from her balance and adds 30 to Bob's balance. Two state entries are updated. The transaction is a single unit of work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UTXO model:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alice's wallet selects one or more UTXOs that sum to at least 30 tokens. Suppose she has a single UTXO worth 50 tokens. The transaction consumes that UTXO and creates two new ones: 30 tokens owned by Bob and 20 tokens owned by Alice as change. One state entry is destroyed, two are created.&lt;/p&gt;

&lt;p&gt;The UTXO approach creates more on-chain objects, but each object reveals less about the overall distribution of funds. An observer sees a 50 token UTXO disappear and a 30 and 20 appear, but they cannot trivially tell which is the payment and which is the change without additional analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Choose Contract-State Accounting
&lt;/h3&gt;

&lt;p&gt;Choose this model when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application needs frequent balance queries from the contract itself&lt;/li&gt;
&lt;li&gt;You are building something familiar like a token with standard wallet integrations&lt;/li&gt;
&lt;li&gt;The user experience of seeing a single balance matters more than transaction level privacy&lt;/li&gt;
&lt;li&gt;Your total number of holders is reasonably bounded&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Choose the UTXO Model
&lt;/h3&gt;

&lt;p&gt;Choose this model when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy is a primary design goal and you want to avoid balance correlation&lt;/li&gt;
&lt;li&gt;Your application benefits from parallel transaction processing&lt;/li&gt;
&lt;li&gt;You need strong guarantees about supply preservation that are enforced by the transaction structure itself&lt;/li&gt;
&lt;li&gt;You are building payment systems or exchange mechanisms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hybrid Approaches
&lt;/h2&gt;

&lt;p&gt;You are not forced to pick one model exclusively. Many applications combine elements of both patterns.&lt;/p&gt;

&lt;p&gt;For example, a DApp might store user balances as a map for quick lookups while also issuing UTXO style receipts for individual transactions. The balance map provides convenience, and the receipts provide auditability.&lt;/p&gt;

&lt;p&gt;Here is a sketch of how that might look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export ledger balanceMap: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Uint&amp;lt;64&amp;gt;&amp;gt;;
export ledger transactionReceipts: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;

witness localSk(): Bytes&amp;lt;32&amp;gt;;

export circuit deposit(amount: Opaque&amp;lt;Uint&amp;lt;64&amp;gt;&amp;gt;): [] {
    const _sk = localSk();
    let user = getDappPublicKey(_sk);

    let currentBalance = balanceMap.lookup(user);
    let newBalance = currentBalance + disclose(amount);
    balanceMap.insert(user, newBalance);

    let receiptId = persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "receipt:"),
        _sk
    ]);
    let receiptData = pad(64, "deposit");
    transactionReceipts.insert(disclose(receiptId), receiptData);
}

export circuit getDappPublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "hybrid:pk:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The balance map handles the operational needs of the application. The receipt map provides an immutable audit trail. Each solves a different problem, and together they offer more than either alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Recommendations
&lt;/h2&gt;

&lt;p&gt;Based on these models, here is a decision framework for your own contracts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start with contract-state accounting if you are building a straightforward token or a DApp where user experience and developer simplicity are priorities. The pattern is easier to get right and easier to debug.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Move to the UTXO model if you discover privacy or concurrency requirements that the account model cannot satisfy. The additional complexity is worth it when these properties matter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consider a hybrid approach if you need both operational efficiency and an audit trail. Just be clear about which state serves which purpose.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regardless of which model you choose, apply domain separated hashing for all user identifiers. This prevents cross DApp tracking and preserves the privacy guarantees you are building into your contract logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test your assumptions on a local Devnet before deploying to a public testnet. Both models behave differently under load, and early testing catches architecture problems before they become expensive.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Privacy Note on Both Models
&lt;/h2&gt;

&lt;p&gt;Neither model automatically guarantees privacy. In contract-state accounting, if you disclose user public keys directly in the balance map, every balance becomes linkable to every transaction that user makes. In the UTXO model, if you reuse the same owner key across multiple UTXOs, you create the same linkability problem.&lt;/p&gt;

&lt;p&gt;Always derive DApp specific public keys using domain separation. Consider whether you can use commitments instead of raw values. And apply the selective disclosure audit checklist covered in the companion tutorial on disclosure patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Understanding these two models gives you a foundation for designing value transfer systems on Midnight. To deepen your knowledge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Study the &lt;a href="https://docs.midnight.network/examples/contracts/token-transfers" rel="noopener noreferrer"&gt;Token Transfers example&lt;/a&gt; in the Midnight documentation to see a production oriented implementation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Experiment with both patterns using the &lt;code&gt;create-mn-app&lt;/code&gt; scaffolding tool. Build the same simple token with each model and compare the developer experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Join the &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Midnight Discord&lt;/a&gt; and share your implementation. The community is actively discussing these design patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply the selective disclosure audit checklist from the companion tutorial to whichever model you choose. Privacy is not automatic, it requires deliberate design.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Contract-state accounting and the UTXO model are both valid paths. The right choice depends on your application requirements and your users needs. Now you have the tools to make that choice confidently.&lt;/p&gt;

</description>
      <category>midnightchallenge</category>
      <category>compact</category>
      <category>utxo</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Selective Disclosure Patterns in Compact: A Developer's Guide</title>
      <dc:creator>Ameer Abdulaleem</dc:creator>
      <pubDate>Mon, 27 Apr 2026 12:14:42 +0000</pubDate>
      <link>https://dev.to/ameer_0699/selective-disclosure-patterns-in-compact-a-developers-guide-3lel</link>
      <guid>https://dev.to/ameer_0699/selective-disclosure-patterns-in-compact-a-developers-guide-3lel</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiq5u8ksgc1c2zc1g2boq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiq5u8ksgc1c2zc1g2boq.jpg" alt="Selective Disclosure Patterns in Compact: A Developer's Guide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  INTRODUCTION
&lt;/h2&gt;

&lt;p&gt;Blockchain transparency is a double edged sword. Every transaction, every state change, and every interaction sits on a public ledger for anyone to inspect. For many applications this is a feature. For applications handling sensitive data it is a dealbreaker.&lt;/p&gt;

&lt;p&gt;Midnight solves this with a dual state architecture. Your DApp maintains both public state visible on chain and private state stored locally with users. The bridge between these two worlds is selective disclosure: the ability to prove something is true about your private data without revealing the data itself.&lt;/p&gt;

&lt;p&gt;This tutorial covers the patterns that make selective disclosure work in Compact. You will learn how to use &lt;code&gt;disclose()&lt;/code&gt; correctly, understand what information is safe to expose, implement domain separated hashing to prevent user tracking across different properties, and apply a systematic privacy audit to your own contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you continue, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Compact toolchain installed (follow the &lt;a href="https://docs.midnight.network/getting-started/installation" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A basic understanding of Compact syntax (review the &lt;a href="https://docs.midnight.network/getting-started/hello-world" rel="noopener noreferrer"&gt;Hello World tutorial&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.npmjs.com/package/midnight-mcp" rel="noopener noreferrer"&gt;Midnight MCP&lt;/a&gt; tool available for validating your code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is Selective Disclosure?
&lt;/h2&gt;

&lt;p&gt;In traditional smart contract development every input to a function becomes public the moment a transaction is submitted. There is no privacy. The contract sees everything and so does everyone else.&lt;/p&gt;

&lt;p&gt;Compact changes this. Circuit parameters are private by default. Consider this simple function signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;circuit&lt;/span&gt; &lt;span class="nf"&gt;storeMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Opaque&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// newMessage is private here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Opaque&amp;lt;"string"&amp;gt;&lt;/code&gt; type tells the compiler that &lt;code&gt;newMessage&lt;/code&gt; is a private input. It exists in the user's local execution environment but never appears on chain in its raw form.&lt;/p&gt;

&lt;p&gt;Selective disclosure happens when you decide to move data from the private context to the public ledger. You do this with the &lt;code&gt;disclose()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export circuit storeMessage(newMessage: Opaque&amp;lt;"string"&amp;gt;): [] {
    message = disclose(newMessage);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line makes a deliberate choice. The message content becomes public state stored in the &lt;code&gt;message&lt;/code&gt; ledger variable. Everyone can now read it.&lt;/p&gt;

&lt;p&gt;But selective disclosure is about more than just toggling visibility. It is about revealing proofs instead of raw data. You can disclose that a condition is met without disclosing the condition itself. You can disclose a hash commitment now and reveal the preimage later. You can disclose aggregated results while keeping individual inputs hidden.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;disclose()&lt;/code&gt; Usage Patterns
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;disclose()&lt;/code&gt; function is the only mechanism for moving data from private context to public state. Understanding its patterns is fundamental to building privacy preserving DApps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: Direct Disclosure
&lt;/h3&gt;

&lt;p&gt;The simplest pattern. You have private data and you want it on chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;

export ledger publicCounter: Counter;

export circuit increment(amount: Opaque&amp;lt;Uint&amp;lt;32&amp;gt;&amp;gt;): [] {
    let increment_by = disclose(amount);
    publicCounter.increment(increment_by);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the user provides a private &lt;code&gt;amount&lt;/code&gt;. The circuit discloses it and increments the public counter. The amount becomes visible to everyone. Use this pattern only when the data is intentionally non sensitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: Conditional Disclosure with Assertions
&lt;/h3&gt;

&lt;p&gt;Often you want to disclose something only after verifying it meets certain conditions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;

export ledger allowedAmount: Uint&amp;lt;32&amp;gt;;

export circuit submitAmount(amount: Opaque&amp;lt;Uint&amp;lt;32&amp;gt;&amp;gt;): [] {
    let value = disclose(amount);
    assert(value &amp;lt;= allowedAmount, "Amount exceeds allowed limit");
    // Proceed with business logic using the disclosed value
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The assertion runs in the ZK circuit. If it fails the entire transaction reverts. The amount is disclosed but only after proving it satisfies the constraint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 3: Disclosing Derived Values
&lt;/h3&gt;

&lt;p&gt;This is where selective disclosure becomes powerful. Instead of disclosing raw inputs you disclose a computed result that reveals only what is necessary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export ledger userEligibility: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, bool&amp;gt;;

export circuit proveEligibility(
    birthYear: Opaque&amp;lt;Uint&amp;lt;16&amp;gt;&amp;gt;,
    currentYear: Uint&amp;lt;16&amp;gt;
): [] {
    let age = currentYear as Uint&amp;lt;32&amp;gt; - (birthYear as Uint&amp;lt;32&amp;gt;);
    let isAdult = age &amp;gt;= 18;

    const _sk = localSk();
    let pubKey = getDappPublicKey(_sk);

    userEligibility.insert(disclose(pubKey), disclose(isAdult));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user provides their birth year as a private input. The circuit computes their age and determines adulthood status. Only the boolean result &lt;code&gt;isAdult&lt;/code&gt; is disclosed. The actual birth year never leaves the private context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 4: Commit Now, Reveal Later
&lt;/h3&gt;

&lt;p&gt;This two phase pattern is essential for auctions, voting, and any scenario requiring a commitment period.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export ledger commitment: Bytes&amp;lt;32&amp;gt;;
export ledger revealedValue: Uint&amp;lt;32&amp;gt;;

export circuit commit(value: Opaque&amp;lt;Uint&amp;lt;32&amp;gt;&amp;gt;, salt: Opaque&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;): [] {
    let hash = persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        value as Bytes&amp;lt;32&amp;gt;,
        salt
    ]);
    commitment = disclose(hash);
}

export circuit reveal(value: Opaque&amp;lt;Uint&amp;lt;32&amp;gt;&amp;gt;, salt: Opaque&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;): [] {
    let computedHash = persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        value as Bytes&amp;lt;32&amp;gt;,
        salt
    ]);
    assert(computedHash == commitment, "Commitment mismatch");

    revealedValue = disclose(value);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the commit phase the user submits a hash of their value and a salt. In the reveal phase they provide both original inputs. The circuit verifies the hash matches before disclosing the value. This prevents front running and ensures users cannot change their submission after seeing others.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Safe to Disclose vs What Leaks Privacy
&lt;/h2&gt;

&lt;p&gt;The line between safe and unsafe disclosure is not always obvious. Here is a framework for evaluating your disclosure decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safe to Disclose
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Aggregates and counts.&lt;/strong&gt; The total number of participants in an event reveals nothing about individual identities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boolean results of private checks.&lt;/strong&gt; Knowing that someone is over 18 does not reveal their exact age.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public keys derived with DApp specific hashing.&lt;/strong&gt; See the domain separation section below for why this matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hashes and commitments.&lt;/strong&gt; A hash without its preimage reveals nothing about the underlying data assuming sufficient entropy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enumerated states.&lt;/strong&gt; Disclosing that an auction is &lt;code&gt;OPEN&lt;/code&gt; or &lt;code&gt;CLOSED&lt;/code&gt; is necessary for coordination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unsafe to Disclose
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Direct personal identifiers.&lt;/strong&gt; Names, email addresses, government ID numbers. Never put these on chain even in encrypted form without careful consideration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linkable pseudonyms.&lt;/strong&gt; If you disclose the same public key across multiple interactions within the same DApp you create a linkage profile. Always use DApp specific key derivation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Precise numeric values when only a range is needed.&lt;/strong&gt; Disclosing an exact salary of 84750 when you only need to prove it exceeds 50000 leaks unnecessary information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correlatable timestamps.&lt;/strong&gt; The exact block height of a user's interaction combined with other disclosed data can create timing correlation attacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raw private keys or seeds.&lt;/strong&gt; This should be obvious but it bears repeating. Never write &lt;code&gt;disclose(localSk())&lt;/code&gt; in your contract.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Linkability Problem
&lt;/h3&gt;

&lt;p&gt;Consider this vulnerable pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// DO NOT USE - Vulnerable to cross interaction tracking
export circuit registerUser(): [] {
    const _sk = localSk();
    let pubKey = publicKey(_sk);  // Standard public key derivation
    registeredUsers.insert(disclose(pubKey));
}

export circuit submitVote(): [] {
    const _sk = localSk();
    let pubKey = publicKey(_sk);
    // Same pubKey used across circuits
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the same public key appears in multiple contracts or even multiple circuits within the same contract, an observer can link all of that user's activity together. This defeats the privacy guarantees you are trying to provide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Separated Hashing for Cross Property Unlinkability
&lt;/h2&gt;

&lt;p&gt;Domain separation is the technique of generating different identifiers for the same user across different contexts. It ensures that an observer cannot link a user's activity in one part of your DApp to their activity in another.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pattern
&lt;/h3&gt;

&lt;p&gt;The Midnight example contracts demonstrate this pattern consistently. Here is the implementation from the Private Guest List contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export circuit getDappPublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "guest-list:pk:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user's private key is hashed with a domain specific string. The resulting public key is unique to this DApp. Even if the same user interacts with another DApp their identifier will be completely different because the domain string differs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Domains Within a Single DApp
&lt;/h3&gt;

&lt;p&gt;You can extend this pattern to create separate unlinkable identities for different features within the same DApp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export circuit getVoterPublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "election:voter:"),
        _sk
    ]);
}

export circuit getCandidatePublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "election:candidate:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A user who is both a voter and a candidate in the same election DApp will have two different unlinkable public keys. Their voting activity cannot be correlated with their candidate registration.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Battleship Example
&lt;/h3&gt;

&lt;p&gt;The Battleship contract uses domain separation to prevent cross game tracking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit getDappPubKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "battleship:pk:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each game instance could further extend this by incorporating a game ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit getGameSpecificPubKey(_sk: Bytes&amp;lt;32&amp;gt;, gameId: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;3, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "battleship:game:"),
        gameId,
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the same player has a different unlinkable identity in every game they play.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy Audit Checklist for Developers
&lt;/h2&gt;

&lt;p&gt;Before deploying a Compact contract conduct this systematic privacy audit. Each question forces you to examine a specific aspect of your disclosure patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Disclosure Inventory
&lt;/h3&gt;

&lt;p&gt;List every occurrence of &lt;code&gt;disclose()&lt;/code&gt; in your contract. For each one answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What specific data is being disclosed?&lt;/li&gt;
&lt;li&gt;Why must this data be public?&lt;/li&gt;
&lt;li&gt;Could a proof of a property replace the raw data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you cannot justify why something must be public reconsider whether it belongs on chain at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Linkability Analysis
&lt;/h3&gt;

&lt;p&gt;For each disclosed identifier ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this identifier appear in multiple circuits within the same contract?&lt;/li&gt;
&lt;li&gt;Does this identifier appear in other contracts deployed by this DApp?&lt;/li&gt;
&lt;li&gt;Could an observer correlate this identifier with activity in other DApps?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any answer is yes implement domain separation using &lt;code&gt;persistentHash&lt;/code&gt; with a unique domain string.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Timing Attack Surface
&lt;/h3&gt;

&lt;p&gt;Examine your contract for timing sensitive disclosures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are there state transitions that reveal when specific users act?&lt;/li&gt;
&lt;li&gt;Do you disclose counts or sizes that change in predictable ways based on user behavior?&lt;/li&gt;
&lt;li&gt;Could block height combined with other disclosures create a timing fingerprint?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider adding random delays or batching operations where appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Commitment Scheme Validation
&lt;/h3&gt;

&lt;p&gt;If your contract uses commit reveal patterns verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is a salt or nonce included in the commitment hash?&lt;/li&gt;
&lt;li&gt;Does the salt have sufficient entropy? Hardcoded or predictable salts offer no protection.&lt;/li&gt;
&lt;li&gt;Is the reveal phase protected against replay attacks?&lt;/li&gt;
&lt;li&gt;What prevents a user from refusing to reveal after the commitment period?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Aggregation Check
&lt;/h3&gt;

&lt;p&gt;When disclosing aggregate data verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the aggregate sufficiently large to provide k anonymity? A group of two is not private.&lt;/li&gt;
&lt;li&gt;Could an attacker manipulate inputs to isolate a specific user's contribution?&lt;/li&gt;
&lt;li&gt;Are you disclosing the aggregate before all contributions are finalized?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Cross Circuit Information Flow
&lt;/h3&gt;

&lt;p&gt;Consider what an observer learns by comparing the public outputs of different circuits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does circuit A disclose something that circuit B later proves about the same user?&lt;/li&gt;
&lt;li&gt;Could timing analysis across circuits create a linkage?&lt;/li&gt;
&lt;li&gt;Do different circuits use the same derived public key?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Emergency Stop Consideration
&lt;/h3&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a privacy vulnerability is discovered can the contract be paused?&lt;/li&gt;
&lt;li&gt;Do users have a way to withdraw or nullify their private data if needed?&lt;/li&gt;
&lt;li&gt;Is there a mechanism for users to rotate their identifiers?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  8. Documentation Completeness
&lt;/h3&gt;

&lt;p&gt;Finally verify that your contract includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comments explaining what each disclosed value represents&lt;/li&gt;
&lt;li&gt;Documentation of the privacy guarantees and limitations&lt;/li&gt;
&lt;li&gt;Clear warnings about what information becomes public and when&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Complete Example: Private Age Verification
&lt;/h2&gt;

&lt;p&gt;Let us put these patterns together into a complete contract that demonstrates selective disclosure principles correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version 0.22;
import CompactStandardLibrary;

export ledger verifiedAdults: Set&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;;
export ledger verificationCount: Counter;

witness localSk(): Bytes&amp;lt;32&amp;gt;;

constructor() {
    // No constructor initialization needed
}

export circuit verifyAge(
    birthYear: Opaque&amp;lt;Uint&amp;lt;16&amp;gt;&amp;gt;,
    currentYear: Uint&amp;lt;16&amp;gt;
): [] {
    // Compute age without disclosing birth year
    let age = currentYear as Uint&amp;lt;32&amp;gt; - (birthYear as Uint&amp;lt;32&amp;gt;);
    let isAdult = age &amp;gt;= 18;

    // Only disclose the boolean result
    assert(disclose(isAdult), "You must be 18 or older");

    // Generate DApp specific public key for unlinkability
    const _sk = localSk();
    let dappPubKey = getDappPublicKey(_sk);

    // Record verification without linking to other DApps
    verifiedAdults.insert(disclose(dappPubKey));
    verificationCount.increment(1);
}

export circuit getDappPublicKey(_sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "age-verification:pk:"),
        _sk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contract demonstrates several best practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user's birth year is private and never disclosed&lt;/li&gt;
&lt;li&gt;Only the boolean adulthood status is disclosed through the assertion&lt;/li&gt;
&lt;li&gt;The user is recorded using a DApp specific public key preventing cross DApp tracking&lt;/li&gt;
&lt;li&gt;The verification count is public allowing transparency about total verifications without revealing who was verified&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Follow-up the clear further explanation README of my commit and gets your code compiled easily:  &lt;a href="https://github.com/Ameerabdulaleem/midnight_network_selective_disclosure_patterns.git" rel="noopener noreferrer"&gt;https://github.com/Ameerabdulaleem/midnight_network_selective_disclosure_patterns.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selective disclosure is a skill that improves with practice. Here are concrete ways to deepen your understanding:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Study the official examples.&lt;/strong&gt; The &lt;a href="https://docs.midnight.network/examples/contracts/private-guest-list" rel="noopener noreferrer"&gt;Private Guest List&lt;/a&gt;, &lt;a href="https://docs.midnight.network/examples/contracts/election" rel="noopener noreferrer"&gt;Election&lt;/a&gt;, and &lt;a href="https://docs.midnight.network/examples/contracts/private-reserve-auction" rel="noopener noreferrer"&gt;Private Reserve Auction&lt;/a&gt; contracts all demonstrate sophisticated disclosure patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use the Midnight MCP tool.&lt;/strong&gt; Before submitting any work, run your Compact code through &lt;code&gt;midnight-mcp&lt;/code&gt; to validate compilation and catch common mistakes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Join the community.&lt;/strong&gt; The &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Midnight Discord&lt;/a&gt; and &lt;a href="https://forum.midnight.network/" rel="noopener noreferrer"&gt;Developer Forum&lt;/a&gt; are active with developers working through the same privacy challenges you are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Apply the audit checklist to your own contracts.&lt;/strong&gt; The best way to internalize these patterns is to critically examine your own code.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Selective disclosure is what makes Midnight uniquely suited for real world applications that require both decentralization and data protection. Master these patterns and you will build DApps that are not just functional but genuinely private.&lt;/p&gt;

</description>
      <category>midnight</category>
      <category>compact</category>
      <category>zeroknowledge</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Rust Ownership Explained for Solidity Developers: The Mindset Shift That Changes Everything in Web3</title>
      <dc:creator>Ameer Abdulaleem</dc:creator>
      <pubDate>Mon, 30 Mar 2026 14:37:34 +0000</pubDate>
      <link>https://dev.to/ameer_0699/rust-ownership-explained-for-solidity-developers-the-mindset-shift-that-changes-everything-in-web3-4j68</link>
      <guid>https://dev.to/ameer_0699/rust-ownership-explained-for-solidity-developers-the-mindset-shift-that-changes-everything-in-web3-4j68</guid>
      <description>&lt;p&gt;If you have been writing smart contracts in Solidity and now want to learn Rust for Solana, Polkadot, or NEAR, you will feel a big change. The syntax is different, but the real challenge is how you think about memory.&lt;/p&gt;

&lt;p&gt;In Solidity the Ethereum Virtual Machine handles memory for you. Rust asks you to manage memory yourself with a simple but powerful system called &lt;strong&gt;Ownership&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This one concept is the biggest mindset shift. Once you understand it, the rest of Rust feels natural and safe. Let me explain it step by step in plain English.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 3 Key Things About Rust Ownership
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ownership Rules&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Every piece of data has exactly one owner. When that owner goes out of scope, Rust automatically drops the data and frees the memory. No garbage collector needed. This keeps your program fast and predictable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Borrowing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You do not always need to move ownership. You can borrow the data instead.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;T&lt;/code&gt; is an immutable borrow – many people can read it at the same time.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;mut T&lt;/code&gt; is a mutable borrow – only one person can change it at a time.
The compiler checks these rules before your code runs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lifetimes&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rust tracks how long a reference stays valid. It does this at compile time so you never use data that has already been dropped. This stops dangerous bugs like “use after free”.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Simple Code Examples You Can Try Right Now
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Example 1: Ownership Move&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Solana is fast"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                    &lt;span class="c1"&gt;// ownership moves to s2&lt;/span&gt;
    &lt;span class="c1"&gt;// println!("{}", s1);          // This line will not compile&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;             &lt;span class="c1"&gt;// Only s2 can use the data now&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 2: Borrowing in Action&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;Rustfn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rust for Web3"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reader1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// anyone can read&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reader2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// multiple readers are fine&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// only one writer allowed&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" developers"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 3: Lifetimes (easy version)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;Rustfn&lt;/span&gt; &lt;span class="n"&gt;longest&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;first&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;second&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Think of Memory Like a Library Book&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine every piece of data is a book in a library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ownership = only one library card per book. When you finish with the book, it goes back on the shelf automatically.&lt;/li&gt;
&lt;li&gt;Borrowing = you can read the book (&amp;amp;T) or write notes in it (&amp;amp;mut T). Many people can read at once, but only one person can write.&lt;/li&gt;
&lt;li&gt;Lifetimes = the due date on the card. The librarian (Rust compiler) checks the date before you leave so the book never disappears while someone is still using it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This system feels strict at first, but it protects you. You get memory safety without slowing down your code – perfect for high-speed blockchains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters for Real Web3 Projects&lt;/strong&gt;&lt;br&gt;
In blockchain, one small memory bug can lose millions of dollars. Rust’s ownership rules catch those bugs before your contract even runs. That is why Solana, Polkadot, and NEAR chose Rust. Companies building serious infrastructure want developers who understand this safety mindset.&lt;br&gt;
How to Practice Ownership Today&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Write a small Rust program that moves ownership and see the compiler error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change it to use borrowing and watch the error disappear.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Try the longest function above with strings of different lengths.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do this for 15 minutes and you will feel the shift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
Learning Rust ownership is like learning to drive a manual car after years of automatic. It feels harder at first, but once you get it, you have much more control and confidence.&lt;/p&gt;

&lt;p&gt;This is only the second article in my Rust for Web3 series. &lt;/p&gt;

&lt;p&gt;Next we will cover Traits and how they let you build reusable DeFi components.&lt;/p&gt;

&lt;p&gt;If this helped you understand the mindset shift, drop a comment below. Tell me what part was confusing before and clear now. I read every comment and I love helping new Web3 developers.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>ownership</category>
      <category>web3</category>
      <category>solidity</category>
    </item>
    <item>
      <title>The Three Reasons RUST is Winning Web3 in 2026: Safety, Performance, and Fearless Concurrency</title>
      <dc:creator>Ameer Abdulaleem</dc:creator>
      <pubDate>Sun, 29 Mar 2026 20:02:22 +0000</pubDate>
      <link>https://dev.to/ameer_0699/why-rust-is-taking-over-web3-development-in-2026-memory-safety-zero-cost-abstractions-and-8bl</link>
      <guid>https://dev.to/ameer_0699/why-rust-is-taking-over-web3-development-in-2026-memory-safety-zero-cost-abstractions-and-8bl</guid>
      <description>&lt;p&gt;If you've been following blockchain development lately, you've probably noticed a clear trend: major projects like Solana, Polkadot, and NEAR are all built using Rust. &lt;/p&gt;

&lt;p&gt;This is not just hype. Rust solves fundamental challenges when building secure, high-performance decentralized systems where a single bug can cost millions.&lt;/p&gt;

&lt;p&gt;At its core, Rust brings two revolutionary features that make it perfect for blockchain infrastructure.&lt;/p&gt;

&lt;p&gt;First, Rust delivers guaranteed memory safety without a garbage collector. This means the compiler catches dangerous bugs like null pointer dereferencing and data races at compile time, before your code ever runs. In blockchain, where security is non-negotiable, this protection is essential.&lt;/p&gt;

&lt;p&gt;Second, Rust offers zero-cost abstractions. You can write clean, high-level code that feels modern and readable, yet it compiles down to performance that rivals hand-written C or C++. For blockchains that need to handle thousands of transactions per second, this combination of readability and raw speed is a game-changer.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Three Core Advantages of Rust
&lt;/h3&gt;

&lt;p&gt;Here are the three pillars that make Rust stand out for Web3:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Memory Safety Guarantees&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rust's ownership system and borrow checker ensure that common catastrophic bugs (null pointers, data races) are impossible at runtime. The compiler acts like a strict security auditor, stopping these issues before deployment. This is why teams building critical infrastructure trust Rust.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero-Cost Abstractions&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You get high-level features like iterators and pattern matching without any performance penalty. The code you write reads beautifully but runs as efficiently as low-level C. This lets developers focus on logic instead of micro-optimizations while still hitting the speed blockchains demand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fearless Concurrency&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Writing safe parallel code is notoriously error-prone in most languages. Rust's type system enforces thread safety at compile time, so you can confidently use modern multi-core processors for validators, indexers, and high-throughput nodes without subtle bugs that are hard to debug.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Think of It Like Building a Championship Race Car
&lt;/h3&gt;

&lt;p&gt;Building a blockchain is like engineering a high-stakes race car. You need blazing speed (performance) but zero tolerance for mechanical failure (security).  &lt;/p&gt;

&lt;p&gt;Other languages often force a trade-off: build something fast that might crash, or something safe that is too slow.  &lt;/p&gt;

&lt;p&gt;Rust lets you have both. Its memory safety provides an unbreakable chassis, zero-cost abstractions deliver advanced aerodynamics for top speed, and fearless concurrency ensures perfect engine synchronization. You get maximum performance without compromising reliability.&lt;/p&gt;

&lt;p&gt;This is exactly why Solana, Polkadot, NEAR, and many other high-performance chains chose Rust for their core infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started with Rust for Web3
&lt;/h3&gt;

&lt;p&gt;If you're coming from JavaScript, Python, or Solidity, the biggest mindset shift is embracing ownership and borrowing. Once that clicks, the rest (Cargo, traits, error handling with Result and Option) builds on a solid, safe foundation.&lt;/p&gt;

&lt;p&gt;Rust is not just another language trend. It is an architectural upgrade for building the next generation of scalable, secure blockchains.&lt;/p&gt;

&lt;p&gt;What has been your experience learning Rust for Web3? Are you building on Solana, Polkadot, NEAR, or another ecosystem? Share your thoughts or questions in the comments.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags&lt;/strong&gt;: rust, web3, blockchain, solana, polkadot, programming, rustlang, systemsprogramming&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cover Image Suggestion&lt;/strong&gt;: Use one of your existing carousels or race car analogy visuals (dark theme works great on Dev.to). If you need a new one generated, let me know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Series Note&lt;/strong&gt;: This is the first article in a series on Rust for Web3 developers. &lt;br&gt;
Next up: Deep dive into Rust Ownership explained like a library.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>web3</category>
      <category>webdev</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
