<?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: Midnight Aliit Fellowship</title>
    <description>The latest articles on DEV Community by Midnight Aliit Fellowship (@midnight-aliit).</description>
    <link>https://dev.to/midnight-aliit</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%2Forganization%2Fprofile_image%2F12781%2Fd7b48f1d-42ed-4b7a-9dd2-6595b3b750f7.png</url>
      <title>DEV Community: Midnight Aliit Fellowship</title>
      <link>https://dev.to/midnight-aliit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/midnight-aliit"/>
    <language>en</language>
    <item>
      <title>Compact Is Not TypeScript. That's the Whole Point.</title>
      <dc:creator>Tushar Pamnani</dc:creator>
      <pubDate>Thu, 23 Apr 2026 08:44:50 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/compact-is-not-typescript-thats-the-whole-point-3d9</link>
      <guid>https://dev.to/midnight-aliit/compact-is-not-typescript-thats-the-whole-point-3d9</guid>
      <description>&lt;p&gt;Most developers approach Compact with the wrong frame.&lt;/p&gt;

&lt;p&gt;They see the syntax - functions, types, imports, curly braces, and conclude: &lt;em&gt;this is basically TypeScript with some ZK stuff sprinkled in.&lt;/em&gt; Then they start writing. And things break in ways that don't make sense. Loops that should work don't compile. Logic that feels correct fails silently. Private data bleeds into places it shouldn't.&lt;/p&gt;

&lt;p&gt;The syntax didn't lie to them. Their mental model did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Illusion Is the Lesson
&lt;/h2&gt;

&lt;p&gt;Here's a Compact circuit:&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 get(): Uint&amp;lt;64&amp;gt; {
  assert(state == State.SET, "Value not set");
  return value;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your brain reads this and fires the usual pattern: &lt;em&gt;function, assertion, return value.&lt;/em&gt; It looks like a getter. Call it, get a thing back.&lt;/p&gt;

&lt;p&gt;But that's not what's happening.&lt;/p&gt;

&lt;p&gt;This circuit doesn't "run." It declares a set of constraints. When you call it from your dApp, the proof system generates a zero-knowledge proof that those constraints were satisfied, without revealing the inputs. What goes on-chain isn't the output. It's the proof.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The frame shift:&lt;/strong&gt; You're not writing code that executes. You're writing a description of a valid state, and the system proves you were in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Mental Models. One Will Break You.
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Traditional code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input → Code executes → Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You write instructions. The machine follows them. You get a result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compact circuits:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input → Constraints are declared → Proof is generated → Proof is verified
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are no "instructions" in the traditional sense. The circuit defines relationships. The prover proves the relationships held. The chain verifies the proof without seeing the inputs.&lt;/p&gt;

&lt;p&gt;Nothing is executed. Correctness is proved.&lt;/p&gt;

&lt;p&gt;This is why &lt;code&gt;assert&lt;/code&gt; is your only runtime guard. It's not just input validation, it's the mechanism by which you define what a valid state even is. Every &lt;code&gt;assert&lt;/code&gt; is a constraint. Every constraint becomes part of the circuit. The circuit is the proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Constraints Are the Feature
&lt;/h2&gt;

&lt;p&gt;The first reaction most developers have to Compact's restrictions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No recursion&lt;/li&gt;
&lt;li&gt;Bounded loops only (bounds must be compile-time constants)&lt;/li&gt;
&lt;li&gt;Fixed type sizes&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;any&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Unsigned types only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;"These seem arbitrary. Why can't I just-"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They're not arbitrary. They're causal.&lt;/p&gt;

&lt;p&gt;ZK proofs require finite circuits. A circuit is a fixed structure; gates, wires, constraints, determined entirely at compile time. If you could write unbounded loops or recursion, the compiler couldn't produce a circuit. The circuit literally couldn't exist. The proof couldn't be generated.&lt;/p&gt;

&lt;p&gt;The constraints don't limit what Compact can do. They're what make the magic possible at all.&lt;/p&gt;

&lt;p&gt;Accept this, and the language makes complete sense. Fight it, and you'll spend weeks trying to port TypeScript patterns into a tool designed around a completely different model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Privacy" Actually Means Here
&lt;/h2&gt;

&lt;p&gt;Compact is not just "TypeScript with encryption." The privacy model is structural.&lt;/p&gt;

&lt;p&gt;Midnight has two worlds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;World&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Who can see it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Public&lt;/td&gt;
&lt;td&gt;On-chain&lt;/td&gt;
&lt;td&gt;Everyone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private&lt;/td&gt;
&lt;td&gt;Local&lt;/td&gt;
&lt;td&gt;Only you&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your sensitive data; balances, credentials, secrets, never leaves your machine. What goes on-chain is a proof that you ran the computation correctly on that private data. Validators verify the proof. They never see the inputs.&lt;/p&gt;

&lt;p&gt;This has a concrete implication: &lt;strong&gt;you have to know the boundary.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;export ledger&lt;/code&gt; is public. &lt;code&gt;witness&lt;/code&gt; data is private. &lt;code&gt;disclose()&lt;/code&gt; is the explicit line you cross when you intentionally move private data into public state.&lt;/p&gt;

&lt;p&gt;The compiler enforces this. If private data tries to flow into public state without going through &lt;code&gt;disclose()&lt;/code&gt;, it fails. But knowing &lt;em&gt;why&lt;/em&gt; this boundary exists — not just that it does — is what lets you design contracts correctly from the start instead of debugging your way there.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Use Cases This Unlocks
&lt;/h2&gt;

&lt;p&gt;This matters because entire categories of blockchain applications were previously impossible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare:&lt;/strong&gt; Prove you meet age or eligibility requirements without revealing your date of birth or medical history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Finance:&lt;/strong&gt; Prove your balance exceeds a threshold for a loan without revealing the balance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity:&lt;/strong&gt; Prove citizenship or credential validity without revealing the underlying document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competitive markets:&lt;/strong&gt; Submit bids or inventory levels that can be verified as legitimate without being revealed to competitors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't niche edge cases. They're the reason regulated industries haven't fully adopted public blockchains. With Compact, the privacy model that makes these use cases viable is built into the language, not bolted on after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Comparison Breaks Down
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;TypeScript&lt;/th&gt;
&lt;th&gt;Solidity&lt;/th&gt;
&lt;th&gt;Compact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Private data&lt;/td&gt;
&lt;td&gt;Convention only&lt;/td&gt;
&lt;td&gt;On-chain (visible)&lt;/td&gt;
&lt;td&gt;Stays local, always&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Functions&lt;/td&gt;
&lt;td&gt;Execute instructions&lt;/td&gt;
&lt;td&gt;Execute instructions&lt;/td&gt;
&lt;td&gt;Declare constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loops&lt;/td&gt;
&lt;td&gt;Unbounded&lt;/td&gt;
&lt;td&gt;Unbounded (gas-limited)&lt;/td&gt;
&lt;td&gt;Bounded at compile time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recursion&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;td&gt;Not allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy enforcement&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Type system + compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The closest thing Compact resembles isn't TypeScript. It's a constraint satisfaction system with TypeScript syntax. The syntax is a DX choice, not a design philosophy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Thing to Take Away
&lt;/h2&gt;

&lt;p&gt;Every concept in Compact, witnesses, circuits, &lt;code&gt;disclose()&lt;/code&gt;, the boundedness rules, becomes clear once you have the right frame:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're not writing programs. You're describing valid states and proving you were in them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With that frame:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Witnesses make sense: they're the private inputs the prover uses to generate the proof.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;disclose()&lt;/code&gt; makes sense: it's the intentional act of moving a constraint into public view.&lt;/li&gt;
&lt;li&gt;Bounded loops make sense: the circuit is fixed at compile time, so everything has to be fixed at compile time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assert&lt;/code&gt; makes sense: it's not validation, it's constraint declaration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without that frame, everything in Compact feels like TypeScript with arbitrary restrictions. With it, everything is exactly as it should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built for This
&lt;/h2&gt;

&lt;p&gt;Most documentation explains the &lt;em&gt;what&lt;/em&gt;. Compact's own docs are good. But the gap isn't information, it's the conceptual layer underneath.&lt;/p&gt;

&lt;p&gt;I've been building a structured guide that goes through each concept with the execution model shift front and center: &lt;strong&gt;&lt;a href="https://github.com/tusharpamnani/Compact-Book" rel="noopener noreferrer"&gt;Compact Book&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're trying to actually understand Compact rather than just cargo-cult your way through examples, start there.&lt;/p&gt;

&lt;p&gt;The syntax is the easy part. The mental model is the work. Get that right, and the rest follows.&lt;/p&gt;

</description>
      <category>midnight</category>
      <category>compact</category>
      <category>web3</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Working with Maps and Merkle Trees in Compact:</title>
      <dc:creator>Nasihudeen Jimoh</dc:creator>
      <pubDate>Thu, 16 Apr 2026 08:19:24 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/working-with-maps-and-merkle-trees-in-compact-40i3</link>
      <guid>https://dev.to/midnight-aliit/working-with-maps-and-merkle-trees-in-compact-40i3</guid>
      <description>&lt;h2&gt;
  
  
  A Guide to the State Dichotomy
&lt;/h2&gt;

&lt;p&gt;The Midnight blockchain introduces a fundamental architectural shift in smart contract design through its "State Dichotomy." Unlike transparency-first blockchains, Midnight enables developers to partition data into public Ledger State and private Shielded State. This guide provides a comprehensive analysis of the two primary data structures used to manage these states: &lt;strong&gt;Maps&lt;/strong&gt; and &lt;strong&gt;Merkle Trees&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Through a dual-implementation approach featuring a public registry and an anonymous allowlist this guide explores the operational mechanics, security considerations, and SDK integration patterns required to build applications with Compact.&lt;/p&gt;




&lt;h2&gt;
  
  
  The State Dichotomy: Conceptual Framework
&lt;/h2&gt;

&lt;p&gt;Compact contracts operates as a decentralized state machine where transitions are governed by Zero-Knowledge (ZK) circuits. The efficiency and privacy of these transitions depend on the selection of appropriate storage structures. This bifurcation of state is not merely an optimization but a core privacy Primitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ledger state (Public)
&lt;/h3&gt;

&lt;p&gt;The Ledger state consists of on chain data structures that are globally visible and replicated across the network nodes. Ledger state is governed by Abstract Data Types (ADTs) such as Maps, Counters, and Sets. These structures provide the shared source of truth required for applications like token supply management, administrative registries, and public consensus variables. In Compact, every ledger interaction is verifiable by any observer, ensuring that the global state remains consistent and auditable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shielded state (Private)
&lt;/h3&gt;

&lt;p&gt;Shielded state remains off chain, residing within the prover’s local environment. Users interact with the ledger by submitting Zero Knowledge (ZK) proofs that verify a state transition has occurred according to the contract's logic without revealing the underlying data. This enables features like confidential assets, anonymous membership verification, and private governance. The shielded state is protected by the prover's secret keys and is only revealed through explicit "disclosures" within a circuit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the correct structure
&lt;/h3&gt;

&lt;p&gt;The choice between a Map and a Merkle Tree is determined by the required visibility of the relationship between a user and their data. Developers must ask: "Does the network need to know &lt;em&gt;who&lt;/em&gt; owns this data, or only &lt;em&gt;that&lt;/em&gt; they own it?"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maps&lt;/strong&gt; are used when the association between a key and a value must be public and directly queryable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merkle Trees&lt;/strong&gt; are used when the goal is to prove membership within a set or the integrity of a dataset without revealing the identity of the specific member.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Associative storage with Maps
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Map&lt;/code&gt; ADT is the primary tool for associative storage on the Midnight ledger. It allows for O(1) lookups and insertions, functioning as a decentralized dictionary. Maps are foundational for any application where identities need to be linked to properties or permissions in a transparent way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syntax and declaration
&lt;/h3&gt;

&lt;p&gt;In Compact, a Map is declared within a &lt;code&gt;ledger&lt;/code&gt; block. The following definition maps a 32-byte public key to a 32-byte profile hash:&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 &amp;gt;= 0.16 &amp;amp;&amp;amp; &amp;lt;= 0.21;

import CompactStandardLibrary;

export ledger registry: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Map operators and the disclosure rule
&lt;/h3&gt;

&lt;p&gt;Every interaction with a Map must navigate the boundary between private circuit parameters and public ledger state.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The disclosure requirement: Bridging Private and Public
&lt;/h4&gt;

&lt;p&gt;Circuit parameters are private by default. In the Compact execution model, parameters are "witnesses" known only to the prover. To store a parameter in a public Map, it must be explicitly disclosed using the &lt;code&gt;disclose()&lt;/code&gt; operator. Failure to do so results in a compilation error, as Compact prevents the accidental leakage of private witnesses into public storage.&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 register(profile_hash: Bytes&amp;lt;32&amp;gt;): [] {
    const user = ownPublicKey();
    const d_profile_hash = disclose(profile_hash);
    registry.insert(user.bytes, d_profile_hash);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that the user is aware of exactly what information is being pushed to the ledger. If you were to attempt &lt;code&gt;registry.insert(user.bytes, profile_hash)&lt;/code&gt;, the compiler would signal a security violation, maintaining a strict "Privacy by Default" posture.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Detailed Map Operations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;insert(key, value)&lt;/code&gt;&lt;/strong&gt;: Creates or updates an entry. If the key already exists, the old value is overwritten. This operation generates a ledger state update that is broadcast to the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lookup(key)&lt;/code&gt;&lt;/strong&gt;: Retrieves the value associated with a key. If the key is absent, it returns the type-specific default (e.g., zero bytes for &lt;code&gt;Bytes&amp;lt;32&amp;gt;&lt;/code&gt;, false for &lt;code&gt;Boolean&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;member(key)&lt;/code&gt;&lt;/strong&gt;: A Boolean operator that checks for key existence without retrieving the value. This is highly efficient for access control checks where the value itself is irrelevant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;remove(key)&lt;/code&gt;&lt;/strong&gt;: Deletes an entry from the ledger, clearing the associated storage. This is crucial for managing "state bloat" and ensuring that outdated memberships are purged.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Zero Knowledge membership with Merkle Trees
&lt;/h2&gt;

&lt;p&gt;Merkle Trees are the engine of anonymity on Midnight. By representing a large set of data with a single 32-byte root hash, they allow for membership verification that preserves the privacy of the specific leaf.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical specification and Depth Selection
&lt;/h3&gt;

&lt;p&gt;Merkle Trees in Compact are fixed-depth. The choice of depth determines the maximum capacity of the tree ($2^{depth}$).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ledger allowlist: MerkleTree&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A depth of 20 supports approximately 1,048,576 entries. Choosing the correct depth is an engineering trade-off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Higher Depth&lt;/strong&gt;: Increases the capacity of the system but linearly increases the size of the ZK circuit and the time required for proof generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower Depth&lt;/strong&gt;: Reduces proving time but risks hitting a "Full State" where no new members can be added without a contract migration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operational lifecycle: The path to proof
&lt;/h3&gt;

&lt;p&gt;The verification of membership involves a transition from the ledger's public root to the prover’s private Merkle path.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Merkle path witness
&lt;/h4&gt;

&lt;p&gt;To prove membership, a user must provide a &lt;strong&gt;Merkle Path&lt;/strong&gt; a sequence of sibling hashes representing a branch from the leaf to the root. This is defined as a &lt;code&gt;witness&lt;/code&gt; function, identifying it as data retrieved from the prover's local environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;witness get_membership_path(leaf: Bytes&amp;lt;32&amp;gt;): MerkleTreePath&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the SDK, this path is calculated by iterating over the local view of the tree and finding the siblings for the target leaf at each level of the binary structure.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Circuit-level verification logic
&lt;/h4&gt;

&lt;p&gt;Inside the ZK-circuit, the &lt;code&gt;merkleTreePathRoot&lt;/code&gt; operator reconstructs the root hash from the provided path and leaf. The circuit verifies that the hashes align at each level ($H(L, R)$).&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 access_exclusive_area(leaf: Bytes&amp;lt;32&amp;gt;): [] {
    const d_leaf = disclose(leaf);
    const path = get_membership_path(d_leaf);

    // ZK Re-computation of the root
    const computed_root = merkleTreePathRoot&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;(path);

    // Verification against Ledger State
    assert(allowlist.checkRoot(disclose(computed_root)), "Access Denied");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Double Disclosure" security pattern
&lt;/h3&gt;

&lt;p&gt;The pattern above utilizes two disclosures that are essential for the Midnight security model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Disclosing the Leaf&lt;/strong&gt;: Ensures the proof corresponds to the exact data provided by the user. If the leaf were not disclosed, a malicious prover could use a different leaf than the one they claim to possess.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Disclosing the Root&lt;/strong&gt;: The &lt;code&gt;checkRoot&lt;/code&gt; operator must compare the &lt;code&gt;computed_root&lt;/code&gt; against the public ledger. For this comparison to occur, the value being checked must be public. Because the root hash is already public, this disclosure does not reveal any private information about the leaf or path used. It simply confirms: "I know a secret that hashes to this public root."&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Technical comparison: Maps vs. Merkle Trees
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Map (Ledger ADT)&lt;/th&gt;
&lt;th&gt;Merkle Tree (Ledger ADT)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fully Public&lt;/td&gt;
&lt;td&gt;Root Public; Leaves/Paths Private&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key-Based (Direct)&lt;/td&gt;
&lt;td&gt;Path-Based (Indirect)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Privacy Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transparent Association&lt;/td&gt;
&lt;td&gt;Set Membership Anonymity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Proof Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low (O(1))&lt;/td&gt;
&lt;td&gt;High (O(depth) hashes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Registries, Public Indices&lt;/td&gt;
&lt;td&gt;Anonymous Allowlists, Confidential Voting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Bloat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Linear per entry&lt;/td&gt;
&lt;td&gt;Fixed per depth ($O(1)$ on-chain root)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Computational overhead analyze
&lt;/h3&gt;

&lt;p&gt;Merkle Trees impose a higher verification cost. A tree of depth 20 requires 20 sequential hash computations within the ZK-circuit. Each hash operation increases the number of constraints in the proof, which correlates directly to proof generation time. While this provides anonymity, developers must consider that a user on a mobile device may take significantly longer to generate a proof for a depth 32 tree compared to a depth-16 tree. In contrast, Map operations are computationally negligible within a circuit.&lt;/p&gt;




&lt;h2&gt;
  
  
  SDK implementation: The structural validation
&lt;/h2&gt;

&lt;p&gt;When integrating Compact contracts with the Midnight TypeScript SDK, developers must align the orchestrator's output with the runtime's expected data shapes. A common failure point is the &lt;strong&gt;Structural Validation&lt;/strong&gt; of the Merkle path.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;instanceof&lt;/code&gt; requirement and Type Safety
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@midnight-ntwrk/compact-runtime&lt;/code&gt; performs strict type checking on objects returned by witness functions. Manually constructing a Merkle path object as a JSON literal will fail even if the fields (&lt;code&gt;value&lt;/code&gt;, &lt;code&gt;alignment&lt;/code&gt;) match. This is because the runtime's ZK IR (Intermediate Representation) requires an instance of the specific internal &lt;code&gt;MerkleTreePath&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Instead, developers must use the &lt;code&gt;findPathForLeaf&lt;/code&gt; method provided by the ledger context.&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="c1"&gt;// src/prover/allowlist_witnesses.ts&lt;/span&gt;
&lt;span class="nf"&gt;get_membership_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This returns an instance of the MerkleTreePath class&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ledger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findPathForLeaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Leaf discovery failed: State mismatch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;path&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;This ensuring that the path is derived from the current ledger state and satisfies the runtime's internal &lt;code&gt;instanceof&lt;/code&gt; checks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Detailed Implementation Walkthrough
&lt;/h2&gt;

&lt;p&gt;The following implementation show how both structures are managed in a single application lifecycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Registry contract: &lt;code&gt;contract/registry.compact&lt;/code&gt;
&lt;/h3&gt;



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

import CompactStandardLibrary;

// Map Bytes&amp;lt;32&amp;gt; address to Bytes&amp;lt;32&amp;gt; profile CID
export ledger registry: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;

export circuit register(profile_hash: Bytes&amp;lt;32&amp;gt;): [] {
    const user = ownPublicKey();
    // Public disclosure for ledger storage
    const d_profile_hash = disclose(profile_hash);
    registry.insert(user.bytes, d_profile_hash);
}

export circuit remove_registration(): [] {
    const user = ownPublicKey();
    registry.remove(user.bytes);
}

export circuit get_profile(user_pk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
    // Disclosure required for circuit parameters used in lookups
    return registry.lookup(disclose(user_pk));
}

export circuit is_registered(user_pk: Bytes&amp;lt;32&amp;gt;): Boolean {
    return registry.member(disclose(user_pk));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allowlist contract: &lt;code&gt;contract/allowlist.compact&lt;/code&gt;
&lt;/h3&gt;



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

import CompactStandardLibrary;

// Merkle Tree for anonymous membership
export ledger allowlist: MerkleTree&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;;

export circuit update_allowlist(member_hash: Bytes&amp;lt;32&amp;gt;): [] {
    const d_member = disclose(member_hash);
    allowlist.insert(d_member);
}

export circuit access_exclusive_area(leaf: Bytes&amp;lt;32&amp;gt;): [] {
    const d_leaf = disclose(leaf);
    const path = get_membership_path(d_leaf);

    // Hash up the tree to the root
    const computed_root = merkleTreePathRoot&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;(path);

    // Check if the computed root matches the current ledger state
    assert(allowlist.checkRoot(disclose(computed_root)), "Access Denied");
}

witness get_membership_path(leaf: Bytes&amp;lt;32&amp;gt;): MerkleTreePath&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TypeScript orchestrator: &lt;code&gt;src/index.ts&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createActionCircuitContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;dummyContractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@midnight-ntwrk/compact-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RegistryContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./managed/registry/contract/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AllowlistContract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./managed/allowlist/contract/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AllowlistProver&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./prover/allowlist_witnesses.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alicePk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profileHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Map Interaction: Public Registry&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegistryContract&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;regCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createActionCircuitContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;dummyContractAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alicePk&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialContractState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialPrivateState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Insert Alice into the registry Map&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updatedRegCtx&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;circuits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;regCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;profileHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Registry state updated via Map insertion.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Merkle Interaction: Anonymous Allowlist&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AllowlistProver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AllowlistContract&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// Resolve the witness using the prover logic&lt;/span&gt;
    &lt;span class="na"&gt;get_membership_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;prover&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_membership_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leaf&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createActionCircuitContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;dummyContractAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alicePk&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialContractState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initialPrivateState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Updating on-chain Merkle root...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Populate the tree before proving&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postAddCtx&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;circuits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update_allowlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;allowCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;alicePk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Verifying anonymous membership proof...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Execute the anonymous access circuit&lt;/span&gt;
  &lt;span class="nx"&gt;allowlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;circuits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;access_exclusive_area&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postAddCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alicePk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access Granted: Membership verified anonymously.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  State Bounded Merkle Trees
&lt;/h2&gt;

&lt;p&gt;Forsystems processing high volumes, a static Merkle Tree can present challenges during concurrent updates. Production Midnight applications often utilize &lt;strong&gt;State Bounded transitions&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Synchronization window
&lt;/h3&gt;

&lt;p&gt;Because the Merkle root is global, every new addition invalidates the old root. If a user is half way through generating a 2 second proof and another transaction lands, the root will shift and the proof will fail.&lt;/p&gt;

&lt;p&gt;Midnight addresses this with &lt;strong&gt;Historic Windows&lt;/strong&gt;. A &lt;code&gt;HistoricMerkleTree&lt;/code&gt; allows a circuit to verify a proof against any root within the last $N$ blocks. This providing a "synchronization window" that makes DApps resilient to high traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Bounded Merkle Trees
&lt;/h3&gt;

&lt;p&gt;A State Bounded Merkle Tree allows for the separation of the state into bounded regions. This is particularly useful for optimizing storage, as old branches that are no longer being proven against can be pruned from active memory while maintaining the cryptographic root consistency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Disclosure hygiene
&lt;/h3&gt;

&lt;p&gt;In Compact, the order of &lt;code&gt;disclose()&lt;/code&gt; calls can influence the circuit's logic flow. Developers should disclose values as late as possible ideally directly before the ledger operation that requires them. This maintain a clear boundary between the private computation (witnesses) and the public assertion (disclosures).&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling state lag in production
&lt;/h3&gt;

&lt;p&gt;In a live network environment, the ledger state might be several blocks ahead of your local prover’s view. When retrieving a Merkle path, ensure your client is synchronized with the specific block height that matches the Merkle root currently stored on the ledger. Outdated paths are the most common cause of &lt;code&gt;checkRoot&lt;/code&gt; assertion failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimization: Caching witness results
&lt;/h3&gt;

&lt;p&gt;Merkle path lookups are computationally intensive for the local ledger view. If an application requires frequent verification (e.g., a private chat room), consider caching the &lt;code&gt;MerkleTreePath&lt;/code&gt; in the private state and only updating it when the ledger's Merkle root changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Structure&lt;/th&gt;
&lt;th&gt;Return Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;insert(k, v)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inserts or updates a value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lookup(k)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;&lt;code&gt;V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieves value or default.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;member(k)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks for key existence.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove(k)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deletes a key from the ledger.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;insert(leaf)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MerkleTree&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Appends a leaf to the tree.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;checkRoot(root)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MerkleTree&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verifies a root against state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;merkleTreePathRoot(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Witness&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bytes&amp;lt;32&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Computes root from path in ZK.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Maps and Merkle Trees are the fundamental storage structures that enable the state dichotomy of the Midnight blockchain. &lt;strong&gt;Maps&lt;/strong&gt; provide the efficiency and directness required for public association, while &lt;strong&gt;Merkle Trees&lt;/strong&gt; facilitate the zero-knowledge membership proofs that define privacy preserving interaction.&lt;/p&gt;

&lt;p&gt;By mastering the transition between these two domains specifically the nuances of the &lt;code&gt;disclose()&lt;/code&gt; operator and the &lt;code&gt;MerkleTreePath&lt;/code&gt; witness resolver developers can architect complex, privacy centric applications that benefit from both transparency and confidentiality. For further exploration, consult the &lt;a href="https://docs.midnight.network" rel="noopener noreferrer"&gt;official Midnight Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The full code example is here you can check it up*&lt;em&gt;GitHub Repository&lt;/em&gt;*: &lt;a href="https://github.com/Kanasjnr/compact-maps-merkle-tutorial" rel="noopener noreferrer"&gt;Kanasjnr/compact-maps-merkle-tutorial&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>blockchain</category>
      <category>privacy</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Hit Midnight's Block Limits Twice; And It Forced Me to Rethink Everything</title>
      <dc:creator>Tushar Pamnani</dc:creator>
      <pubDate>Thu, 16 Apr 2026 08:18:30 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/i-hit-midnights-block-limits-twice-and-it-forced-me-to-rethink-everything-1jki</link>
      <guid>https://dev.to/midnight-aliit/i-hit-midnights-block-limits-twice-and-it-forced-me-to-rethink-everything-1jki</guid>
      <description>&lt;p&gt;&lt;em&gt;This isn't about my prediction market. It's about the assumption that broke it, twice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I want to be upfront about what this is and isn't.&lt;/p&gt;

&lt;p&gt;It's not a tutorial. It's not a "look what I built" post. It's what actually happened when I tried to bring EVM thinking into Midnight, hit a wall, optimized my way into the same wall, and finally understood why the wall existed in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What "Building on Midnight" Did to My Brain (Before It Fixed It)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you spend enough time on EVM, you internalize a model without realizing it. Contracts can compute. Contracts can store structured data. Contracts can iterate. If it compiles, it probably runs.&lt;/p&gt;

&lt;p&gt;I didn't question any of that when I started building on Midnight. I translated the mental model directly across.&lt;/p&gt;

&lt;p&gt;That was the first mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Built (V1)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I built what felt like a clean, well-structured prediction market. Store every bet. Track every user. Compute rewards on-chain. Store results. Let users claim.&lt;/p&gt;

&lt;p&gt;Here's the Market contract state from V1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ledger admin: Bytes&amp;lt;32&amp;gt;;
export ledger marketName: Opaque&amp;lt;"string"&amp;gt;;
export ledger description: Opaque&amp;lt;"string"&amp;gt;;
export ledger imageUrl: Opaque&amp;lt;"string"&amp;gt;;
export ledger parameter: Opaque&amp;lt;"string"&amp;gt;;
export ledger category: Opaque&amp;lt;"string"&amp;gt;;
export ledger bets: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Bet&amp;gt;;
export ledger betKeys: Map&amp;lt;Uint&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;
export ledger betCount: Counter;
export ledger totalVolume: Uint&amp;lt;128&amp;gt;;
export ledger totalParticipants: Uint&amp;lt;64&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Bet struct itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export struct Bet {
  user: Bytes&amp;lt;32&amp;gt;;
  amount: Uint&amp;lt;128&amp;gt;;
  predictedValue: Uint&amp;lt;64&amp;gt;;
  rewardAmount: Uint&amp;lt;128&amp;gt;;
  claimed: Boolean;
  timestamp: Uint&amp;lt;64&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Factory was doing the same thing, storing a full &lt;code&gt;MarketConfig&lt;/code&gt; struct with name, category, contract address, fee snapshot, timestamps, status, and active flag. All on-chain. Registered and tracked in a &lt;code&gt;Map&amp;lt;Uint&amp;lt;32&amp;gt;, MarketConfig&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point, everything felt right. Clean abstractions. Proper data modelling. Structured state. Good EVM design, basically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The First Time It Broke&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It didn't fail at compile time. It failed at execution.&lt;/p&gt;

&lt;p&gt;Some transactions took minutes. Some never completed. Some failed silently. And eventually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BlockLimitExceeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My first instinct: RPC issue? Wallet issue? SDK bug?&lt;/p&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Reality I Didn't Understand&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Midnight doesn't work like EVM. It doesn't charge you more for complexity, it just refuses to execute if you cross limits. The actual constraints are approximately 1 MB transaction size, around 1 second compute time, limited state writes, and constraint-based execution throughout.&lt;/p&gt;

&lt;p&gt;This means you're not optimizing cost. You're trying to fit inside a box. And I wasn't even close.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Actually Went Wrong (Not the Obvious Stuff)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The problem wasn't too many lines of code or too many functions. It was deeper than that.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Structs are not cheap.&lt;/em&gt; In EVM, &lt;code&gt;Bet memory bet = bets[user]&lt;/code&gt; feels like a cheap read. In Midnight, &lt;code&gt;bets.lookup(userPk)&lt;/code&gt; pulls the entire struct into the circuit, every field, every time. So when &lt;code&gt;claimReward()&lt;/code&gt; did this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const userBet = bets.lookup(disclose(userPk));
assert(!userBet.claimed, "PredictionMarket: Already claimed");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It wasn't reading one boolean. It was processing all six fields of the &lt;code&gt;Bet&lt;/code&gt; struct inside the circuit. And then to mark it claimed, I had to reconstruct the entire struct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const updatedBet = Bet {
  user: userBet.user,
  amount: userBet.amount,
  predictedValue: userBet.predictedValue,
  rewardAmount: userBet.rewardAmount,
  claimed: true,
  timestamp: userBet.timestamp
};
bets.insert(disclose(userPk), disclose(updatedBet));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read full struct. Modify one field. Write full struct back. That pattern was everywhere in V1.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I built a database on-chain.&lt;/em&gt; &lt;code&gt;marketName&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;imageUrl&lt;/code&gt;, &lt;code&gt;parameter&lt;/code&gt;, &lt;code&gt;category&lt;/code&gt;, all stored as on-chain ledger state. That's not a contract. That's a backend disguised as a contract.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I designed for iteration.&lt;/em&gt; This line in &lt;code&gt;placeBet&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert(
  betCount.read() &amp;lt; 500 as Uint&amp;lt;64&amp;gt;,
  "PredictionMarket: Max participants reached"
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems harmless. But it implies I was thinking in datasets, bounded participant sets, total user counts, potential future iteration. That's an EVM mindset. In Midnight, you don't think in datasets. You think in constraints.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I tried to compute rewards on-chain.&lt;/em&gt; The V1 &lt;code&gt;Bet&lt;/code&gt; struct stored &lt;code&gt;rewardAmount&lt;/code&gt; per user. That implied reward computation logic would run on-chain. Even before I got there, the struct overhead alone was enough to blow the limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Second Failure (More Important Than the First)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After V1 failed, I did what most developers do. I split contracts. Reduced some redundant state. Reorganized logic. Introduced better lifecycle control.&lt;/p&gt;

&lt;p&gt;V2 got significantly more sophisticated. Oracle whitelist for resolution. Dispute window system. Three-phase reward computation: &lt;code&gt;computeRewardBatch&lt;/code&gt;, &lt;code&gt;finalizeRewardBatch&lt;/code&gt;, &lt;code&gt;markRewardsFinalized&lt;/code&gt;. The inverse distance accuracy model where rewards are proportional to how close your prediction was.&lt;/p&gt;

&lt;p&gt;The math was actually elegant. Here's what &lt;code&gt;computeRewardBatch&lt;/code&gt; was trying to verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const absDiff = getAbsDiff64(userBet.predictedValue, finalValue.read());
const denominator = (absDiff + 1 as Uint&amp;lt;64&amp;gt;) as Uint&amp;lt;128&amp;gt;;
const expectedInverseDist = getQuotient128(
  1000000000000000000 as Uint&amp;lt;128&amp;gt;,  // 1e18
  denominator
);
assert(_inverseDist == expectedInverseDist, "V2: Invalid inverse distance");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For every single bet. One circuit call per user.&lt;/p&gt;

&lt;p&gt;The problem wasn't the math. It was that V2 was still making the same fundamental assumption: contracts should compute. I'd changed how things were written, not what the system was doing. The struct reads were still expensive. The Merkle verification I'd introduced was heavy. The state reads and writes per transaction were still high.&lt;/p&gt;

&lt;p&gt;It failed again.&lt;/p&gt;

&lt;p&gt;This time the realization hit properly: I wasn't solving the right problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Shift (V3)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was the turning point. I stopped asking "how do I implement this on-chain?" and started asking "what is the minimum the chain needs to enforce?"&lt;/p&gt;

&lt;p&gt;That question changed everything.&lt;/p&gt;

&lt;p&gt;Look at what the V3 Market contract stores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ledger admin: Bytes&amp;lt;32&amp;gt;;
export ledger status: Status;
export ledger totalPool: Uint&amp;lt;128&amp;gt;;
export ledger endTime: Uint&amp;lt;64&amp;gt;;
export ledger finalValue: Uint&amp;lt;64&amp;gt;;
export ledger rewardPool: Uint&amp;lt;128&amp;gt;;
export ledger feesCollected: Uint&amp;lt;128&amp;gt;;
export ledger minBet: Uint&amp;lt;128&amp;gt;;
export ledger maxBet: Uint&amp;lt;128&amp;gt;;
export ledger betCount: Counter;
export ledger bets: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Uint&amp;lt;128&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line. &lt;code&gt;bets&lt;/code&gt; is now &lt;code&gt;Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Uint&amp;lt;128&amp;gt;&amp;gt;&lt;/code&gt;. Not a struct. Just the amount.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;placeBet&lt;/code&gt; circuit in V3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bets.insert(disclose(userPk), disclose(_betAmount));
betCount.increment(1);
totalPool.write(disclose((totalPool.read() + _betAmount) as Uint&amp;lt;128&amp;gt;));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three operations. That's it. No struct serialization. No rewardAmount field. No timestamp. No predicted value stored on-chain. The prediction is captured in a separate data structure, just what's needed for the Merkle tree built off-chain.&lt;/p&gt;

&lt;p&gt;The V3 Factory went even further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ledger owner: Bytes&amp;lt;32&amp;gt;;
export ledger marketCount: Counter;
export ledger marketExists: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Boolean&amp;gt;;
export ledger marketById: Map&amp;lt;Uint&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;registerMarket&lt;/code&gt; in V1 took six parameters and stored a full &lt;code&gt;MarketConfig&lt;/code&gt; struct. In V3, it takes one parameter, the market address, and stores a boolean:&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 registerMarket(_marketAddress: Bytes&amp;lt;32&amp;gt;): Uint&amp;lt;32&amp;gt; {
  // ...
  marketExists.insert(disclose(_marketAddress), disclose(true));
  marketById.insert(disclose(id), disclose(_marketAddress));
  marketCount.increment(1);
  return id;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Name, category, timestamps, fee snapshot, gone. All of that lives off-chain now, in an indexer.&lt;/p&gt;

&lt;p&gt;And the Distributor, which is the most interesting piece of V3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export ledger distRoots: Map&amp;lt;Uint&amp;lt;64&amp;gt;, Bytes&amp;lt;32&amp;gt;&amp;gt;;
export ledger distPools: Map&amp;lt;Uint&amp;lt;64&amp;gt;, Uint&amp;lt;128&amp;gt;&amp;gt;;
export ledger distDistributed: Map&amp;lt;Uint&amp;lt;64&amp;gt;, Uint&amp;lt;128&amp;gt;&amp;gt;;
export ledger distDeadlines: Map&amp;lt;Uint&amp;lt;64&amp;gt;, Uint&amp;lt;64&amp;gt;&amp;gt;;
export ledger distStatus: Map&amp;lt;Uint&amp;lt;64&amp;gt;, DistStatus&amp;gt;;
export ledger claims: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Boolean&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flat maps instead of struct maps. Each field stored independently, no struct serialization overhead. And &lt;code&gt;claimReward&lt;/code&gt; does exactly ten things: derive user key, compute leaf hash, check not already claimed, check deadline, check pool bounds, check status, accept proof via witness, mark claimed, update distributed amount, transfer funds. Nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the Architecture Actually Looks Like Now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The flow:&lt;/p&gt;

&lt;p&gt;User places bet on-chain. Off-chain: fetch all bets, compute final value, run the inverse distance accuracy model, generate the full reward distribution, build a Merkle tree over it. Admin submits the Merkle root on-chain via &lt;code&gt;submitDistribution&lt;/code&gt;. User claims with a proof, the contract verifies the leaf, checks the pool, marks claimed, transfers.&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 submitDistribution(
  _merkleRoot: Bytes&amp;lt;32&amp;gt;,
  _totalRewardPool: Uint&amp;lt;128&amp;gt;,
  _claimDeadline: Uint&amp;lt;64&amp;gt;
): Uint&amp;lt;64&amp;gt; {
  // flat writes — no struct overhead
  distRoots.insert(disclose(id), disclose(_merkleRoot));
  distPools.insert(disclose(id), disclose(_totalRewardPool));
  distDistributed.insert(disclose(id), disclose(0 as Uint&amp;lt;128&amp;gt;));
  distDeadlines.insert(disclose(id), disclose(_claimDeadline));
  distStatus.insert(disclose(id), disclose(DistStatus.Active));
  // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chain doesn't know how rewards were computed. It doesn't know how many users participated. It doesn't know who won or by how much. It knows exactly one thing: is this leaf in the Merkle tree, and has it been claimed before?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I Had to Delete (This Is the Important Part)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To make V3 work, I had to remove things I thought were essential:&lt;/p&gt;

&lt;p&gt;The full &lt;code&gt;Bet&lt;/code&gt; struct: gone. Replaced with a single &lt;code&gt;Uint&amp;lt;128&amp;gt;&lt;/code&gt; per user.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;betKeys&lt;/code&gt; index map: gone. Off-chain can reconstruct this.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;totalParticipants&lt;/code&gt;, &lt;code&gt;totalVolume&lt;/code&gt; as tracked ledger values: gone. Computable off-chain.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rewardAmount&lt;/code&gt; per user: gone. That's a Merkle leaf now.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;marketName&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;imageUrl&lt;/code&gt;, &lt;code&gt;parameter&lt;/code&gt;, &lt;code&gt;category&lt;/code&gt;: all gone from on-chain storage.&lt;/p&gt;

&lt;p&gt;The entire V2 reward computation pipeline, &lt;code&gt;computeRewardBatch&lt;/code&gt;, &lt;code&gt;finalizeRewardBatch&lt;/code&gt;, &lt;code&gt;markRewardsFinalized&lt;/code&gt;: gone. Replaced by off-chain computation and a Merkle root.&lt;/p&gt;

&lt;p&gt;The oracle whitelist, dispute window, resolution proposal system: simplified into a single admin &lt;code&gt;resolve()&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;Everything I deleted felt essential when I wrote it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Tradeoff&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where most people get uncomfortable.&lt;/p&gt;

&lt;p&gt;V1 mindset: maximize correctness on-chain. V3 reality: maximize feasibility on-chain.&lt;/p&gt;

&lt;p&gt;Yes, there are fewer on-chain guarantees now. The completeness of the reward distribution isn't enforced on-chain. Fairness depends on the off-chain computation being correct. The trust model shifted.&lt;/p&gt;

&lt;p&gt;But here's the thing: V1 and V2 had zero on-chain guarantees in practice, because they never executed. A system that doesn't run protects nothing.&lt;/p&gt;

&lt;p&gt;V3 actually works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Biggest Lesson&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Midnight is not EVM with privacy.&lt;/p&gt;

&lt;p&gt;It's closer to a constraint system that happens to be programmable. And that changes every design decision from the ground up.&lt;/p&gt;

&lt;p&gt;If you're coming from EVM, here's what to unlearn:&lt;/p&gt;

&lt;p&gt;"Contracts should compute logic." They shouldn't, not on Midnight.&lt;/p&gt;

&lt;p&gt;"Store structured data cleanly." Structs are expensive. Flat maps are cheap.&lt;/p&gt;

&lt;p&gt;"Track everything." Track only what the chain needs to enforce.&lt;/p&gt;

&lt;p&gt;"Optimize gas." Wrong problem entirely. You're not optimizing cost, you're fitting inside a box.&lt;/p&gt;

&lt;p&gt;Replace it with: contracts enforce invariants. State must be minimal. Computation belongs off-chain. Every circuit must fit within constraint limits.&lt;/p&gt;

&lt;p&gt;The line that changed everything for me: &lt;em&gt;the chain should not know how something was computed, only whether it is valid.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn't hit Midnight's limits because I wrote bad code.&lt;/p&gt;

&lt;p&gt;V1 was well-structured. V2 was genuinely sophisticated. Both failed because I was solving the wrong problem.&lt;/p&gt;

&lt;p&gt;Once I changed the mental model, the code became simpler, the circuits became smaller, and the system actually ran.&lt;/p&gt;

&lt;p&gt;If you're building on Midnight and want to talk through the architecture before you commit to a direction, or want to see the full repos, find me on &lt;a href="https://x.com/Tushar_Pamnani_" rel="noopener noreferrer"&gt;X&lt;/a&gt; or &lt;a href="https://github.com/tusharpamnani" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The walls are documented. You don't have to hit them yourself.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://dev.to/tusharpamnani/series/37866"&gt;Midnight in Practice series&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/midnight-wallet-kit" rel="noopener noreferrer"&gt;midnight-wallet-kit&lt;/a&gt; · &lt;a href="https://www.npmjs.com/package/mn-scaffold" rel="noopener noreferrer"&gt;mn-scaffold&lt;/a&gt; · &lt;a href="https://midnight-club-zeta.vercel.app" rel="noopener noreferrer"&gt;Midnight Club&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>[Hands-on] Midnight Deep Dive: Start Building Smart Contracts with Compact</title>
      <dc:creator>Haruki Kondo</dc:creator>
      <pubDate>Tue, 14 Apr 2026 15:25:20 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/hands-on-midnight-deep-dive-start-building-smart-contracts-with-compact-1inb</link>
      <guid>https://dev.to/midnight-aliit/hands-on-midnight-deep-dive-start-building-smart-contracts-with-compact-1inb</guid>
      <description>&lt;h1&gt;
  
  
  Introduction: The "Too Transparent" Problem of Blockchains
&lt;/h1&gt;

&lt;p&gt;Since the Bitcoin whitepaper was published, blockchain technology has transformed many industries, from finance to supply chains, thanks to its trustless design, transparency, and tamper resistance.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://bitcoin.org/bitcoin.pdf" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;bitcoin.org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The idea that anyone can validate the same distributed ledger and form a trust network without centralized administrators was truly revolutionary.&lt;/p&gt;

&lt;p&gt;But complete transparency can also become a major weakness.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if confidential enterprise data is exposed to competitors?&lt;/li&gt;
&lt;li&gt;What if personal transaction history is visible to the entire world?&lt;/li&gt;
&lt;li&gt;What if private medical records or voting behavior can be inspected by anyone?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That "too transparent" problem has been one of the biggest barriers preventing public blockchain technology from being widely adopted in enterprise and daily consumer use cases.&lt;/p&gt;

&lt;p&gt;To solve this dilemma, a breakthrough project has emerged from the Cardano ecosystem.&lt;/p&gt;

&lt;p&gt;That project is &lt;strong&gt;Midnight&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Midnight is a Cardano sidechain focused on data protection and privacy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By leveraging cutting-edge cryptography known as Zero-Knowledge Proofs (ZKPs)[^1], Midnight enables us to prove only the facts we need, without revealing anything else.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ZKPs are often discussed in the context of privacy, but they can also reduce computational overhead depending on how they are used.&lt;/p&gt;

&lt;p&gt;Representative examples include &lt;strong&gt;ZkEVM&lt;/strong&gt; and &lt;strong&gt;INTMAX&lt;/strong&gt;, both of which use ZK-based approaches for EVM-related scaling.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://intmax.io/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fframerusercontent.com%2Fimages%2FLLXZAAN66nJM7LQNKLniS8xz0.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://intmax.io/" rel="noopener noreferrer" class="c-link"&gt;
            INTMAX – Stateless Layer for Billions
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            INTMAX is a stateless, privacy-centric Layer 2 on Ethereum that delivers fast, secure, and cost-efficient transactions. Explore the future of private digital payments and empower your transactions with unmatched anonymity and speed.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fframerusercontent.com%2Fimages%2FMdj4kIdvZCP2j0CzIFQ18v2DLcQ.svg"&gt;
          intmax.io
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I recently joined the Midnight Hackathon in London and spent a lot of time exploring Compact. In this article, I share what I learned in a hands-on format: from environment setup to contract implementation, testing, and deployment.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://midnightsummit.io/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmidnightsummit.io%2Fwp-content%2Fuploads%2F2025%2F10%2Fwebsite-image-1450x984-1-scaled.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://midnightsummit.io/" rel="noopener noreferrer" class="c-link"&gt;
            Home - Midnight Summit 2025
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Midnight Summit 2025: An invitation-only ecosystem event with talks, breakouts and a live Hackathon shaping the future of privacy-first blockchain.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmidnightsummit.io%2Fwp-content%2Fuploads%2F2025%2F10%2FArtboard-1-1-150x150.png"&gt;
          midnightsummit.io
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you want a conceptual introduction to Midnight first, check this article:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9" class="crayons-story__hidden-navigation-link"&gt;The Future of Privacy: A Deep Dive into Cardano's Midnight &amp;amp; Zero-Knowledge Proofs&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/midnight-aliit"&gt;
            &lt;img alt="Midnight Aliit Fellowship logo" 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%2Forganization%2Fprofile_image%2F12781%2Fd7b48f1d-42ed-4b7a-9dd2-6595b3b750f7.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/mashharuki" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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%2Fuser%2Fprofile_image%2F986561%2Fded098d5-9fd3-49b5-8045-594b7174fec3.JPG" alt="mashharuki profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/mashharuki" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Haruki Kondo
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Haruki Kondo
                
              
              &lt;div id="story-author-preview-content-3495075" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/mashharuki" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F986561%2Fded098d5-9fd3-49b5-8045-594b7174fec3.JPG" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Haruki Kondo&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/midnight-aliit" class="crayons-story__secondary fw-medium"&gt;Midnight Aliit Fellowship&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9" id="article-link-3495075"&gt;
          The Future of Privacy: A Deep Dive into Cardano's Midnight &amp;amp; Zero-Knowledge Proofs
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/blockchain"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;blockchain&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/web3"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;web3&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/zkp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;zkp&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;[^1]: Zero-Knowledge Proof (ZKP) is a cryptographic method that lets you prove a statement is true without revealing any additional information (including why it is true).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Compact: A Privacy Smart Contract Language with TypeScript-like Syntax
&lt;/h1&gt;

&lt;p&gt;Another core pillar behind Midnight's innovation is its smart contract language, &lt;strong&gt;Compact&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Isn't zero-knowledge technology only for cryptography experts?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Compact is designed to dramatically lower that barrier.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript-based syntax
&lt;/h2&gt;

&lt;p&gt;The biggest feature of Compact is that it is a &lt;strong&gt;TypeScript-based domain-specific language (DSL)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means a large number of web developers can build privacy-preserving applications with familiar syntax, instead of learning an entirely new language from scratch.&lt;/p&gt;

&lt;p&gt;The Compact compiler translates your logic into the cryptographic components needed for zero-knowledge proofs, so you do not have to deal with the underlying math directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three data states: Public, Private, Witness
&lt;/h2&gt;

&lt;p&gt;The core of data handling in Compact is clear separation of privacy levels.&lt;/p&gt;

&lt;p&gt;Data is mainly handled in three states:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNp9Ustu2zAQ_JUFL20BWbAejmy5CJDYzi1FgaSXRjnQ0loiIpECH3Zcw9-QQ3stChT9wH5CKdE23BQoD-KSO5oZDnZHclEgSUkpaVvB_TzjYJcyS3eRkU8KJYjVapBXlHFAvmZS8Aa5zogDd-vq4fePby_QSramGlP46ApQ2n7fL-Ul-qUPxnK9UdCiVILTGnJhuEb5CIPBJVxPHR3y4h8T95JyRXPNBAfGW3OmfW2Vf32HDdMcleqUhVg50Em32VpnuGbCKFjT2iBsqIKR053tZkzmhmnAZ8xNp7H_j5VbVnBWVhqWtcifXChvBXfxvDvPBObW2tef0JplzXLrrN9fRaKFtkGYtujS6vM4uHptYdZfLx4-4ObIdNcxPU7P-zeuf0j_L8CiB8wPp5v-dDUlHmlQNpQVdgh2XS8jusIGM5LasqDyqXvT3uKcyUXBtJAkXdFaoUeo0eJuy3OSamnwCJozavNqTijsf7p1o9ZPnEekMGV1QrSUfxaiOdKUsjPkamkzQDnrsiFpEPVYku7IM0mTxA_H8cUkSKLJRZREsUe2JA2jxE-CeBhEk3g0jkfR3iNfevLAHw7DyTi0ndE4iYdhtP8DPDP2hw" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fpako%3AeNp9Ustu2zAQ_JUFL20BWbAejmy5CJDYzi1FgaSXRjnQ0loiIpECH3Zcw9-QQ3stChT9wH5CKdE23BQoD-KSO5oZDnZHclEgSUkpaVvB_TzjYJcyS3eRkU8KJYjVapBXlHFAvmZS8Aa5zogDd-vq4fePby_QSramGlP46ApQ2n7fL-Ul-qUPxnK9UdCiVILTGnJhuEb5CIPBJVxPHR3y4h8T95JyRXPNBAfGW3OmfW2Vf32HDdMcleqUhVg50Em32VpnuGbCKFjT2iBsqIKR053tZkzmhmnAZ8xNp7H_j5VbVnBWVhqWtcifXChvBXfxvDvPBObW2tef0JplzXLrrN9fRaKFtkGYtujS6vM4uHptYdZfLx4-4ObIdNcxPU7P-zeuf0j_L8CiB8wPp5v-dDUlHmlQNpQVdgh2XS8jusIGM5LasqDyqXvT3uKcyUXBtJAkXdFaoUeo0eJuy3OSamnwCJozavNqTijsf7p1o9ZPnEekMGV1QrSUfxaiOdKUsjPkamkzQDnrsiFpEPVYku7IM0mTxA_H8cUkSKLJRZREsUe2JA2jxE-CeBhEk3g0jkfR3iNfevLAHw7DyTi0ndE4iYdhtP8DPDP2hw%3Ftype%3Dpng"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;public&lt;/code&gt; (public state)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data visible on the blockchain.&lt;/li&gt;
&lt;li&gt;Similar to state variables in conventional smart contracts.&lt;/li&gt;
&lt;li&gt;Defined with the &lt;code&gt;ledger&lt;/code&gt; keyword.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;private&lt;/code&gt; (private state)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confidential data managed only in a user's local (off-chain) environment.&lt;/li&gt;
&lt;li&gt;The raw private data itself is not recorded on-chain.&lt;/li&gt;
&lt;li&gt;Defined with the &lt;code&gt;private&lt;/code&gt; keyword.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;witness&lt;/code&gt; (proof input)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input supplied during transaction execution to prove, "I know this data."&lt;/li&gt;
&lt;li&gt;Used as evidence when updating private state.&lt;/li&gt;
&lt;li&gt;Defined with the &lt;code&gt;witness&lt;/code&gt; keyword.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Basic syntax and a Counter smart contract example
&lt;/h3&gt;

&lt;p&gt;Let's look at these concepts through a simple Counter contract.&lt;/p&gt;

&lt;p&gt;This contract only increments a number, and is also introduced in official tutorials.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A state variable stored on the public ledger&lt;/li&gt;
&lt;li&gt;A method to increment that state
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;language_version&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.16&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CompactStandardLibrary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Public state stored on the on-chain ledger&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Transition function that updates public state&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;circuit&lt;/span&gt; &lt;span class="nf"&gt;increment&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="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ledger&lt;/code&gt;&lt;/strong&gt;:
A publicly visible on-chain state variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;circuit&lt;/code&gt;&lt;/strong&gt;:
A transaction-invoked state transition function where validation and updates happen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Compact, you can write logic in TypeScript-like syntax while controlling data privacy in detail and proving correctness intuitively.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hands-on: Set up your Midnight development environment
&lt;/h2&gt;

&lt;p&gt;Now let's move from theory to practice.&lt;/p&gt;

&lt;p&gt;In this section, we build the environment needed to run &lt;code&gt;counter.compact&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As of November 2025, frontend integration is still unstable.&lt;/p&gt;

&lt;p&gt;So in this hands-on, the goal is to deploy a smart contract and interact with it via CLI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Required components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compact CLI&lt;/strong&gt;
Command-line tool for compiling and testing smart contracts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lace Midnight Preview Wallet&lt;/strong&gt;
Browser extension wallet for Midnight Testnet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testnet Faucet&lt;/strong&gt;
Service to get test tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZK Proof Server&lt;/strong&gt;
Local server for generating and verifying zero-knowledge proofs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sample repository&lt;/strong&gt;
Source code used in this tutorial.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Step 1: Install Compact CLI
&lt;/h3&gt;

&lt;p&gt;First, install the &lt;code&gt;compact&lt;/code&gt; CLI compiler:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then pin a specific version (&lt;code&gt;0.25.0&lt;/code&gt; in this article):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact update 0.25.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Check installation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# compact 0.2.0 or similar&lt;/span&gt;
compact compile &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# 0.25.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If &lt;code&gt;compact compile --version&lt;/code&gt; returns &lt;code&gt;0.25.0&lt;/code&gt;, you're good.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Prepare Lace Wallet and get Testnet tokens
&lt;/h2&gt;

&lt;p&gt;Next, set up a wallet and receive test tokens.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install Lace Wallet&lt;/strong&gt;
Add &lt;a href="https://chromewebstore.google.com/detail/lace-midnight-preview/hgeekaiplokcnmakghbdfbgnlfheichg" rel="noopener noreferrer"&gt;Lace Midnight Preview&lt;/a&gt; from the Chrome Web Store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create your wallet&lt;/strong&gt;
Follow the setup wizard. Store your recovery phrase securely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy your address&lt;/strong&gt;
From the wallet home screen, click "Receive" and copy your address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request faucet funds&lt;/strong&gt;
Open &lt;a href="https://midnight.network/test-faucet" rel="noopener noreferrer"&gt;Midnight Testnet Faucet&lt;/a&gt;, paste your address, and click "Request funds". After a short wait, test &lt;code&gt;tDUST&lt;/code&gt; tokens should arrive.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Step 3: Start ZK Proof Server
&lt;/h3&gt;

&lt;p&gt;Private processing (including proof generation) is handled through a local Proof Server.&lt;/p&gt;

&lt;p&gt;We'll start the official Midnight Docker image:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Docker Desktop must be installed for this step.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 6300:6300 midnightnetwork/proof-server &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;'midnight-proof-server --network testnet'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If logs start streaming, it launched successfully. Keep this terminal running.&lt;/p&gt;

&lt;p&gt;You can also verify with:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"http://localhost:6300"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Expected response:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;We&lt;span class="s1"&gt;'re alive 🎉!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 4: Prepare sample repository
&lt;/h2&gt;

&lt;p&gt;Finally, set up the repository used in this article:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/mashharuki" rel="noopener noreferrer"&gt;
        mashharuki
      &lt;/a&gt; / &lt;a href="https://github.com/mashharuki/midnight-sample" rel="noopener noreferrer"&gt;
        midnight-sample
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      midnightでの開発事前検証用リポジトリ
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;midnight-sample&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;midnightでの開発事前検証用リポジトリ&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;環境&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;yarn&lt;/li&gt;
&lt;li&gt;docker&lt;/li&gt;
&lt;li&gt;compact CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;compact CLIのインストール&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;curl --proto &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;=https&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh &lt;span class="pl-k"&gt;|&lt;/span&gt; sh&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;その後、以下でバージョン指定&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;compact update 0.25.0&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;インストールされているかの確認&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;compact --version
compact compile --version&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;それぞれ以下のようになればOK!&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;compact 0.2.0
0.25.0&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Testnet用のZKProof serverの起動&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker run -p 6300:6300 midnightnetwork/proof-server -- &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;midnight-proof-server --network testnet&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;docker ps&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;localhost:6300でサーバーが起動していればOK&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;CONTAINER ID   IMAGE                          COMMAND                  CREATED          STATUS          PORTS                                         NAMES
a62d9787f7a1   midnightnetwork/proof-server   &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;/nix/store/qa9fb15p…&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;   25 seconds ago   Up 24 seconds   0.0.0.0:6300-&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;6300/tcp, [::]:6300-&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;6300/tcp   flamboyant_roentgen&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;念の為以下のコマンドでも稼働確認&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;curl -X GET &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;http://localhost:6300&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;We&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;re alive 🎉!&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;サンプルプログラムのコンパイル＆デプロイ手順&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;まず、依存関係をインストールする&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;yarn&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;以下のコマンドでコントラクトをビルドする&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;yarn contract compact&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;以下のようになればOK!&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;Fetching public parameters &lt;span class="pl-k"&gt;for&lt;/span&gt; k=10 [&lt;span class="pl-k"&gt;====================&lt;/span&gt;] 192.38 KiB / 192.38 KiB
  circuit &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;increment&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; (k=10, rows=29)  
Overall progress [&lt;span class="pl-k"&gt;====================&lt;/span&gt;] 1/1   &lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;コントラクトのユニットテストコードを実行する&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;yarn contract &lt;span class="pl-c1"&gt;test&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;以下のようになればOK!&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt; RUN  v4.0.8 /workspaces/midnight-sample/my-mn-app/pkgs/contract
 ✓ test/counter.test.ts (3 tests) 44ms
   ✓ Counter smart contract (3)
     ✓ generates initial ledger state deterministically 36ms
     ✓ properly initializes ledger&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/mashharuki/midnight-sample" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone your own fork&lt;/span&gt;
&lt;span class="c"&gt;# (fork the repository first)&lt;/span&gt;
git clone https://github.com/&amp;lt;user-name&amp;gt;/midnight-sample.git
&lt;span class="nb"&gt;cd &lt;/span&gt;midnight-sample

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace the &lt;code&gt;git clone&lt;/code&gt; URL with your actual repository URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Environment setup is now complete.&lt;/p&gt;

&lt;p&gt;Next, let's implement and test the smart contract.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implement and test the Counter contract
&lt;/h1&gt;

&lt;p&gt;With the environment ready, let's build and test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code walkthrough
&lt;/h2&gt;

&lt;p&gt;Put this contract in &lt;code&gt;pkgs/contract/src/counter.compact&lt;/code&gt;:&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="nx"&gt;pragma&lt;/span&gt; &lt;span class="nx"&gt;language_version&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.16&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CompactStandardLibrary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Public state stored on the on-chain ledger&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;ledger&lt;/span&gt; &lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Transition function that updates public state&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;circuit&lt;/span&gt; &lt;span class="nf"&gt;increment&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="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;Then compile with &lt;code&gt;compact&lt;/code&gt; CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn contract compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact compile ./src/counter.compact ./src/managed/counter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On success, you will see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Fetching public parameters &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10 &lt;span class="o"&gt;[====================]&lt;/span&gt; 192.38 KiB / 192.38 KiB
  circuit &lt;span class="s2"&gt;"increment"&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10, &lt;span class="nv"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;29&lt;span class="o"&gt;)&lt;/span&gt;
Overall progress &lt;span class="o"&gt;[====================]&lt;/span&gt; 1/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unit test implementation
&lt;/h2&gt;

&lt;p&gt;Compact lets you simulate contract logic off-chain for testing.&lt;/p&gt;

&lt;p&gt;See &lt;code&gt;pkgs/contract/src/test/counter.test.ts&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CounterSimulator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./counter-simulator.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;NetworkId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;setNetworkId&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@midnight-ntwrk/midnight-js-network-id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;setNetworkId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NetworkId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Undeployed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Unit tests for the Counter contract
 */&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Counter smart contract&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generates initial ledger state deterministically&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;simulator0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CounterSimulator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;simulator1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CounterSimulator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;simulator0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLedger&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;simulator1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLedger&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;properly initializes ledger state and private state&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;simulator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CounterSimulator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialLedgerState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;simulator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLedger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialLedgerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialPrivateState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;simulator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrivateState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialPrivateState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;privateCounter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;increments the counter correctly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;simulator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CounterSimulator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextLedgerState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;simulator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextLedgerState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextPrivateState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;simulator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrivateState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextPrivateState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;privateCounter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests verify three scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contract initialization is deterministic.&lt;/li&gt;
&lt;li&gt;Initial ledger value is &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;increment&lt;/code&gt; updates the counter correctly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Run tests
&lt;/h2&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn contract &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all tests pass, output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;RUN  v4.0.8 /workspaces/midnight-sample/my-mn-app/pkgs/contract

 ✓ &lt;span class="nb"&gt;test&lt;/span&gt;/counter.test.ts &lt;span class="o"&gt;(&lt;/span&gt;3 tests&lt;span class="o"&gt;)&lt;/span&gt; 44ms
   ✓ Counter smart contract &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;
     ✓ generates initial ledger state deterministically 36ms
     ✓ properly initializes ledger state and private state 3ms
     ✓ increments the counter correctly 4ms

 Test Files  1 passed &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
      Tests  3 passed &lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;
   Start at  08:27:47
   Duration  421ms &lt;span class="o"&gt;(&lt;/span&gt;transform 95ms, setup 0ms, collect 233ms, tests 44ms, environment 0ms, prepare 13ms&lt;span class="o"&gt;)&lt;/span&gt;

JUNIT report written to /workspaces/midnight-sample/my-mn-app/pkgs/contract/reports/report.xml
Done &lt;span class="k"&gt;in &lt;/span&gt;1.34s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we know the logic works, let's create the CLI flow to deploy on Testnet.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploy and execute from CLI on Testnet
&lt;/h1&gt;

&lt;p&gt;After local testing, it's time to deploy.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;pkgs/cli&lt;/code&gt; package contains scripts for deployment and contract interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate TypeScript API
&lt;/h2&gt;

&lt;p&gt;First, build the &lt;code&gt;contract&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn contract build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; tsc &lt;span class="nt"&gt;--project&lt;/span&gt; tsconfig.build.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-Rf&lt;/span&gt; ./src/managed ./dist/managed &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; ./src/counter.compact ./dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates typed APIs so circuits like &lt;code&gt;increment&lt;/code&gt; can be called safely from the &lt;code&gt;cli&lt;/code&gt; package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set environment variables
&lt;/h2&gt;

&lt;p&gt;Deploying to Testnet requires the wallet seed used to sign transactions.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;.env&lt;/code&gt; from template in &lt;code&gt;pkgs/cli&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;pkgs/cli/.env.example pkgs/cli/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;pkgs/cli/.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NETWORK_ENV_VAR=testnet
SEED_ENV_VAR=
INITIAL_COUNTER_ENV_VAR=
CACHE_FILE_ENV_VAR=
CONTRACT_ADDRESS=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Handle your seed with extreme care.&lt;/p&gt;

&lt;p&gt;Make sure this file is excluded by &lt;code&gt;.gitignore&lt;/code&gt; and never pushed to GitHub.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  CLI unit test walkthrough
&lt;/h2&gt;

&lt;p&gt;There are also unit tests for the CLI layer.&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="c1"&gt;// This file is part of midnightntwrk/example-counter.&lt;/span&gt;
&lt;span class="c1"&gt;// Copyright (C) 2025 Midnight Foundation&lt;/span&gt;
&lt;span class="c1"&gt;// SPDX-License-Identifier: Apache-2.0&lt;/span&gt;
&lt;span class="c1"&gt;// Licensed under the Apache License, Version 2.0 (the "License");&lt;/span&gt;
&lt;span class="c1"&gt;// You may not use this file except in compliance with the License.&lt;/span&gt;
&lt;span class="c1"&gt;// You may obtain a copy of the License at&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// Unless required by applicable law or agreed to in writing, software&lt;/span&gt;
&lt;span class="c1"&gt;// distributed under the License is distributed on an "AS IS" BASIS,&lt;/span&gt;
&lt;span class="c1"&gt;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;
&lt;span class="c1"&gt;// See the License for the specific language governing permissions and&lt;/span&gt;
&lt;span class="c1"&gt;// limitations under the License.&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@midnight-ntwrk/wallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Wallet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@midnight-ntwrk/wallet-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CounterProviders&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../utils/common-types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currentDir&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../utils/logger-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TestEnvironment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./commons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterAll&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;.log`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logDir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&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="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TestEnvironment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Wallet&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CounterProviders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&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="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;testEnvironment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testConfiguration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWallet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureProviders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dappConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveWalletCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should deploy the contract and increment the counter [@slow]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counterContract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;privateCounter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterContract&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayCounterValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;counterContract&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterValue&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterContract&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txHash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9a-f&lt;/span&gt;&lt;span class="se"&gt;]{64}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blockHeight&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeGreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counterAfter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayCounterValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;counterContract&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterAfter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterValue&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterAfter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run these tests on both local and testnet environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run unit tests locally
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn cli test-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Test Files  1 passed &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
      Tests  1 passed &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
   Start at  08:41:12
   Duration  200.97s &lt;span class="o"&gt;(&lt;/span&gt;transform 180ms, setup 72ms, collect 1.11s, tests 199.62s, environment 0ms, prepare 10ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run unit tests against testnet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn cli test-against-testnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✓ src/test/counter.api.test.ts &lt;span class="o"&gt;(&lt;/span&gt;1 &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; 151857ms
  ✓ API &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
    ✓ should deploy the contract and increment the counter &lt;span class="o"&gt;[&lt;/span&gt;@slow]  125059ms

Test Files  1 passed &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
    Tests  1 passed &lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
  Start at  08:47:54
  Duration  153.65s &lt;span class="o"&gt;(&lt;/span&gt;transform 205ms, setup 93ms, collect 1.56s, tests 151.86s, environment 0ms, prepare 8ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment script walkthrough
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pkgs/cli/scripts/deploy.ts&lt;/code&gt; deploys the contract to Testnet.&lt;/p&gt;

&lt;p&gt;It uses libraries such as &lt;code&gt;@midnight-ntwrk/midnight-sdk&lt;/code&gt; and performs the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load wallet seed from &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Build wallet object from the seed.&lt;/li&gt;
&lt;li&gt;Configure providers for Testnet connection.&lt;/li&gt;
&lt;li&gt;Execute deployment via &lt;code&gt;api.deploy&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Run deployment
&lt;/h2&gt;

&lt;p&gt;Deploy with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn cli deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On success, the deployed contract address appears in terminal output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;12:16:24.603] INFO &lt;span class="o"&gt;(&lt;/span&gt;39506&lt;span class="o"&gt;)&lt;/span&gt;: Deploying counter contract...
&lt;span class="o"&gt;[&lt;/span&gt;12:17:27.488] INFO &lt;span class="o"&gt;(&lt;/span&gt;39506&lt;span class="o"&gt;)&lt;/span&gt;: Deployed contract at address: 020050e6bdae4c9e65023a252a6aba74323c1d9c1ba6e520f00e84a5fc1c75b100f3
&lt;span class="o"&gt;[&lt;/span&gt;12:17:27.488] INFO &lt;span class="o"&gt;(&lt;/span&gt;39506&lt;span class="o"&gt;)&lt;/span&gt;: Deployment transaction: 00000000c408a293e4e287285649623774b2be950bf0d385a20117ce79a99eb7315aa547
&lt;span class="o"&gt;[&lt;/span&gt;12:17:27.489] INFO &lt;span class="o"&gt;(&lt;/span&gt;39506&lt;span class="o"&gt;)&lt;/span&gt;: Contract address: 020050e6bdae4c9e65023a252a6aba74323c1d9c1ba6e520f00e84a5fc1c75b100f3
Counter contract deployed at: 020050e6bdae4c9e65023a252a6aba74323c1d9c1ba6e520f00e84a5fc1c75b100f3
&lt;span class="o"&gt;[&lt;/span&gt;12:17:27.489] INFO &lt;span class="o"&gt;(&lt;/span&gt;39506&lt;span class="o"&gt;)&lt;/span&gt;: Not saving cache as &lt;span class="nb"&gt;sync &lt;/span&gt;cache was not defined
Done &lt;span class="k"&gt;in &lt;/span&gt;90.16s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set that value in &lt;code&gt;CONTRACT_ADDRESS&lt;/code&gt; inside your &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execute &lt;code&gt;increment&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Finally, call the deployed contract's &lt;code&gt;increment&lt;/code&gt; circuit.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn cli increment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script reads &lt;code&gt;CONTRACT_ADDRESS&lt;/code&gt; from &lt;code&gt;.env&lt;/code&gt;, connects to the existing contract with &lt;code&gt;api.joinContract&lt;/code&gt;, then calls &lt;code&gt;api.increment&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If successful, you should see transaction info and the current counter value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;12:33:37.176] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Incrementing...
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.270] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Transaction 000000000202acbcd05e9f19e5144acc5f97953255840b8b932fc71b84520e715b7ca900 added &lt;span class="k"&gt;in &lt;/span&gt;block 2485067
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.271] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Increment transaction: 000000000202acbcd05e9f19e5144acc5f97953255840b8b932fc71b84520e715b7ca900 &lt;span class="o"&gt;(&lt;/span&gt;block 2485067&lt;span class="o"&gt;)&lt;/span&gt;
Counter incremented. &lt;span class="nv"&gt;txId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;000000000202acbcd05e9f19e5144acc5f97953255840b8b932fc71b84520e715b7ca900 &lt;span class="nv"&gt;block&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2485067
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.271] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Checking contract ledger state...
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.462] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Ledger state: 1
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.463] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Current counter value: 1
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.463] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Current counter value: 1
Current counter value: 1
&lt;span class="o"&gt;[&lt;/span&gt;12:34:34.463] INFO &lt;span class="o"&gt;(&lt;/span&gt;47085&lt;span class="o"&gt;)&lt;/span&gt;: Not saving cache as &lt;span class="nb"&gt;sync &lt;/span&gt;cache was not defined
Done &lt;span class="k"&gt;in &lt;/span&gt;128.20s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;Current counter value: 1&lt;/code&gt; appears, your public counter was incremented successfully.&lt;/p&gt;

&lt;p&gt;This completes the hands-on section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current limitations and what's next
&lt;/h2&gt;

&lt;p&gt;As of November 2025, Midnight is still in &lt;strong&gt;developer Testnet&lt;/strong&gt; phase, and Mainnet has not launched yet.&lt;/p&gt;

&lt;p&gt;So keep the following in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;
Finality on Testnet can take time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API changes&lt;/strong&gt;
SDK/CLI behavior may change during active development. Check official docs regularly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature scope&lt;/strong&gt;
Available tooling is still limited, though development is moving quickly with community feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend integration&lt;/strong&gt;
This was the area that took me the most research time during the hackathon. I also confirmed with the Midnight team onsite that stable frontend-contract integration libraries were not yet available at that time, so CLI was the practical path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Midnight is a very ambitious project tackling one of Web3's most important challenges: privacy.&lt;/p&gt;

&lt;p&gt;With Cardano's strong security model and community behind it, this ecosystem is definitely worth watching.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing thoughts
&lt;/h1&gt;

&lt;p&gt;In this article, we walked through &lt;strong&gt;Midnight&lt;/strong&gt;, Cardano's privacy-focused sidechain, and &lt;strong&gt;Compact&lt;/strong&gt;, its smart contract language, in a practical hands-on format.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Midnight's approach to the blockchain "too transparent" problem.&lt;/li&gt;
&lt;li&gt;Compact language for intuitive private DApp development with TypeScript-like syntax.&lt;/li&gt;
&lt;li&gt;Data modeling with &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;, and &lt;code&gt;witness&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Full flow from environment setup to implementation, testing, and Testnet deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am very excited to see how this evolves.&lt;/p&gt;

&lt;p&gt;The hackathon itself was also an amazing experience, so I will keep following upcoming updates.&lt;/p&gt;

&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.midnight.network/" rel="noopener noreferrer"&gt;Midnight official website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/" rel="noopener noreferrer"&gt;Midnight documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/compact" rel="noopener noreferrer"&gt;Compact GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps" rel="noopener noreferrer"&gt;Midnight Awesome DApps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chromewebstore.google.com/detail/lace-midnight-preview/hgeekaiplokcnmakghbdfbgnlfheichg" rel="noopener noreferrer"&gt;Lace Midnight Preview Wallet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://midnight.network/test-faucet" rel="noopener noreferrer"&gt;Midnight Testnet Faucet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://midnight-hackathon.devpost.com/" rel="noopener noreferrer"&gt;Midnight Hackathon (Devpost)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>web3</category>
      <category>zkp</category>
      <category>blockchain</category>
      <category>cardano</category>
    </item>
    <item>
      <title>The Future of Privacy: A Deep Dive into Cardano's Midnight &amp; Zero-Knowledge Proofs</title>
      <dc:creator>Haruki Kondo</dc:creator>
      <pubDate>Mon, 13 Apr 2026 14:08:48 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9</link>
      <guid>https://dev.to/midnight-aliit/the-future-of-privacy-a-deep-dive-into-cardanos-midnight-zero-knowledge-proofs-1nn9</guid>
      <description>&lt;h1&gt;
  
  
  Introduction: The "Too Transparent" Problem in Blockchain
&lt;/h1&gt;

&lt;p&gt;Seventeen years have passed since the Bitcoin whitepaper was released.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://bitcoin.org/bitcoin.pdf" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;bitcoin.org&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Blockchain's immutability and transparency have been praised as groundbreaking mechanisms for trustworthy transactions. Because anyone can verify the ledger, blockchains created a new model of tamper-resistant trust.&lt;/p&gt;

&lt;p&gt;However, that same "complete transparency" also creates barriers in areas involving business and personal privacy. This issue has been especially clear in enterprise settings, which is one reason private chain adoption became popular.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if your full banking transaction history were visible to anyone in the world?&lt;/li&gt;
&lt;li&gt;What if a company's confidential supply chain information were exposed to competitors?&lt;/li&gt;
&lt;li&gt;What if personal medical records or voting history became public?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even imagining this is unsettling. I believe this "too transparent" problem is one of the key reasons blockchain technology has not yet fully penetrated all parts of society.&lt;/p&gt;

&lt;p&gt;Not everything can be public all the time. To solve this dilemma, a new light has emerged: &lt;strong&gt;Midnight&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://docs.midnight.network/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.midnight.network%2Fimg%2Fog-image.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://docs.midnight.network/" rel="noopener noreferrer" class="c-link"&gt;
            Midnight Documentation | Midnight Docs
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Build privacy-preserving applications with selective disclosure and zero-knowledge proofs on Midnight.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.midnight.network%2Fimg%2Ffavicon.ico" width="48" height="48"&gt;
          docs.midnight.network
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Midnight is Cardano's partner chain (sidechain) specialized for data protection and privacy. In this article, I will guide you through Midnight in a story-driven way so you can understand its innovative technology and future potential.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 1: What Is Midnight? - A New Blockchain for Privacy
&lt;/h1&gt;

&lt;p&gt;In one sentence, Midnight is a data-protection blockchain that enables &lt;strong&gt;Selective Disclosure&lt;/strong&gt;. While many traditional blockchains force everything to be public, Midnight makes it possible to &lt;strong&gt;prove only the facts that need to be proven, without revealing anything else&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
There are other blockchains, such as &lt;strong&gt;Zcash&lt;/strong&gt;, that adopt similar privacy-oriented approaches.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The technology that enables this is &lt;strong&gt;Zero-Knowledge Proofs (ZKPs)&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNp1kU1vwjAMhv9K5NMmFdYvWtpJk8bXZUJC2k7QHUJjmog2QWkKYxX_fSkMkCbNJ8d6_L6200KuGEIKhaY7Tj4mmSQ26mb9W9CUCSOUpCUZlSrf5pwKeYG6eF1ZQtY07xjCqKGfpNd7IaN2ukd9NFzIgoia7Jp1KfLT86UTJfvjMxdMioIbchCGkyVq1XuT6lAiK5AstFKb-m46_sd00lpyj0TJ8kgOnJrOWSIyZFfnLqarBeq6W-mpRlnb9WzPXWX2MGtKK8AFYygf7yODAxXqigpm79V25QwMxwozSG3KqN5mkMmT5Zqd1cOpPZ3SkG5oWaMDtDHq_ShzSI1u8ApNBLUnqG4Unpvml185f44DWjUFvxE7KpdKVVeZQncDXXJtx0Q9Vo00kMb-mYW0hS_7ivv-MIwSLw6SKIiD0IEjpH4Q92MvdL0gCQfDcBCcHPg-i3t91_WToe_FkesmsR9Fpx9VmbJ2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fpako%3AeNp1kU1vwjAMhv9K5NMmFdYvWtpJk8bXZUJC2k7QHUJjmog2QWkKYxX_fSkMkCbNJ8d6_L6200KuGEIKhaY7Tj4mmSQ26mb9W9CUCSOUpCUZlSrf5pwKeYG6eF1ZQtY07xjCqKGfpNd7IaN2ukd9NFzIgoia7Jp1KfLT86UTJfvjMxdMioIbchCGkyVq1XuT6lAiK5AstFKb-m46_sd00lpyj0TJ8kgOnJrOWSIyZFfnLqarBeq6W-mpRlnb9WzPXWX2MGtKK8AFYygf7yODAxXqigpm79V25QwMxwozSG3KqN5mkMmT5Zqd1cOpPZ3SkG5oWaMDtDHq_ShzSI1u8ApNBLUnqG4Unpvml185f44DWjUFvxE7KpdKVVeZQncDXXJtx0Q9Vo00kMb-mYW0hS_7ivv-MIwSLw6SKIiD0IEjpH4Q92MvdL0gCQfDcBCcHPg-i3t91_WToe_FkesmsR9Fpx9VmbJ2%3Ftype%3Dpng" width="1204" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Comparison of Traditional Blockchain vs Midnight with ZKPs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I have another article dedicated to &lt;strong&gt;Zero-Knowledge Proofs (ZKPs)&lt;/strong&gt;, so feel free to check that out as well:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://zenn.dev/mashharuki/articles/zk_groth16-plonk" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fzenn%2Fimage%2Fupload%2Fs--yQUTHF49--%2Fc_fit%252Cg_north_west%252Cl_text%3Anotosansjp-medium.otf_55%3A%2525E3%252580%252590%2525E5%25259B%2525B3%2525E8%2525A7%2525A3%2525E3%252580%252591%2525E3%252582%2525BC%2525E3%252583%2525AD%2525E7%25259F%2525A5%2525E8%2525AD%252598%2525E8%2525A8%2525BC%2525E6%252598%25258E%2525E3%252581%2525AEPLONK%2525E3%252581%2525A8%2525E3%252581%2525AF%2525EF%2525BC%25259FGroth16%2525E3%252581%2525A8%2525E3%252581%2525AE%2525E9%252581%252595%2525E3%252581%252584%2525E3%252582%252592%2525E4%2525B8%252596%2525E7%252595%25258C%2525E4%2525B8%252580%2525E3%252582%25258F%2525E3%252581%25258B%2525E3%252582%25258A%2525E3%252582%252584%2525E3%252581%252599%2525E3%252581%25258F%2525E8%2525A7%2525A3%2525E8%2525AA%2525AC%2525EF%2525BC%252581%252Cw_1010%252Cx_90%252Cy_100%2Fg_south_west%252Cl_text%3Anotosansjp-medium.otf_37%3AHaruki%252Cx_203%252Cy_121%2Fg_south_west%252Ch_90%252Cl_fetch%3AaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EtL0FPaDE0R2dIaXowNy12WWVodmV1RVRrNGZoU25LaldfYTVFdmJlYnprWG1XPXM5Ni1j%252Cr_max%252Cw_90%252Cx_87%252Cy_95%2Fv1627283836%2Fdefault%2Fog-base-w1200-v2.png%3F_a%3DBACAGSGT" height="630" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://zenn.dev/mashharuki/articles/zk_groth16-plonk" rel="noopener noreferrer" class="c-link"&gt;
            【図解】ゼロ知識証明のPLONKとは？Groth16との違いを世界一わかりやすく解説！
          &lt;/a&gt;
        &lt;/h2&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.zenn.studio%2Fimages%2Flogo-transparent.png" width="315" height="315"&gt;
          zenn.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  What Midnight aims to achieve
&lt;/h2&gt;

&lt;p&gt;Midnight's mission is to remove the long-standing trade-off between data protection, ownership, and data utility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For users&lt;/strong&gt;: They can fully control their own data and decide who can see what, and how much.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For developers and enterprises&lt;/strong&gt;: They can build innovative data-driven services without taking on privacy-violation risk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This opens the door to use cases that were previously difficult or impossible on public blockchains due to confidentiality requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Midnight's core: architecture and consensus
&lt;/h2&gt;

&lt;p&gt;As a Cardano partner chain, Midnight is built on top of Cardano's robust security infrastructure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architecture&lt;/strong&gt;: Midnight splits smart contract state into two parts:

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Public state&lt;/strong&gt;: Data that remains publicly available on-chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private state&lt;/strong&gt;: Data each user manages off-chain in their own local environment.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kachina protocol&lt;/strong&gt;: Public and private states are linked securely through &lt;strong&gt;Kachina&lt;/strong&gt;, a research-driven unified framework. Users generate ZK proofs locally using private data, then submit only the proof to the blockchain for verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consensus algorithm&lt;/strong&gt;: Midnight adopts a hybrid consensus model combining &lt;strong&gt;AURA&lt;/strong&gt; (block production) and &lt;strong&gt;GRANDPA&lt;/strong&gt; (finality). Cardano stake pool operators (SPOs) participate as block producers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two native tokens: NIGHT and DUST
&lt;/h2&gt;

&lt;p&gt;Midnight uses a unique dual-token model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Characteristics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NIGHT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Governance, consensus&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Unshielded token&lt;/strong&gt;. Tradable and contributes to network security.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DUST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transaction fees (gas)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Shielded resource&lt;/strong&gt;. Non-transferable and privacy-preserving.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;DUST&lt;/code&gt; acts as fuel, but because it is not a tradable asset, transaction metadata privacy is better preserved while keeping service costs more predictable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 2: The Bond with Cardano - Why a Partner Chain?
&lt;/h1&gt;

&lt;p&gt;Midnight is an independent chain, but its deep integration with Cardano is what unlocks its full potential.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNpdkU1zgjAQhv9KZs9oFVCU6XiQeqQ6Y0_VHiJZJaMkzCa0tY7_vQGKts1pk_fJvvtxgUwLhBgOxMuczV-2irljql37kHASXGm2yLQ5G4tFq9cn2XRiyqVSaN_u2nq1NJu15UdkK61Pjzt6mC1LJG41mR8Olfhnlkqh5CG37Bnth6bjPV-6uWlJ7tx-Wc1XZjM_6ezIVqRFleFfgzZMWK_HStLvUqBhBrOKpD2zva6U4FZq5fQZS1u4Lr7hOVmZyZJbZNw0hDNrmbQDFFIvq0tiUll0fdyyJeBBgVRwKdx8L_W_LdgcC9xC7ELBXYewVVfHVaUrAxdCuvlAvOcngx7wyur1WWUQW6qwg54kdy7FjcLmU9pusVmmB6SrQ34jSq5etS66NAeqC2pjcjNCStwYLMTRuGEhvsCnu0V9fxKOp8MomI6DKAg9OEPsB1E_GoaDYTANR5NwFFw9-GqSD_uDgT-d-E4ZTaJw4AfXb6Skvbw" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmermaid.ink%2Fimg%2Fpako%3AeNpdkU1zgjAQhv9KZs9oFVCU6XiQeqQ6Y0_VHiJZJaMkzCa0tY7_vQGKts1pk_fJvvtxgUwLhBgOxMuczV-2irljql37kHASXGm2yLQ5G4tFq9cn2XRiyqVSaN_u2nq1NJu15UdkK61Pjzt6mC1LJG41mR8Olfhnlkqh5CG37Bnth6bjPV-6uWlJ7tx-Wc1XZjM_6ezIVqRFleFfgzZMWK_HStLvUqBhBrOKpD2zva6U4FZq5fQZS1u4Lr7hOVmZyZJbZNw0hDNrmbQDFFIvq0tiUll0fdyyJeBBgVRwKdx8L_W_LdgcC9xC7ELBXYewVVfHVaUrAxdCuvlAvOcngx7wyur1WWUQW6qwg54kdy7FjcLmU9pusVmmB6SrQ34jSq5etS66NAeqC2pjcjNCStwYLMTRuGEhvsCnu0V9fxKOp8MomI6DKAg9OEPsB1E_GoaDYTANR5NwFFw9-GqSD_uDgT-d-E4ZTaJw4AfXb6Skvbw%3Ftype%3Dpng" width="633" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ecosystem Integration
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Strategic integration between Cardano and Midnight Network&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inheriting security
&lt;/h2&gt;

&lt;p&gt;Midnight addresses the challenge of network security by becoming a Cardano partner chain. It leverages Cardano's large, decentralized SPO network, allowing Midnight to access globally distributed, enterprise-grade infrastructure from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Midnight differs from Cardano
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cardano&lt;/strong&gt;: Focuses on value storage/transfer and serving as a secure general-purpose decentralized platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Midnight&lt;/strong&gt;: Uses Cardano's security as a base while specializing in &lt;strong&gt;データ保護とプライバシー&lt;/strong&gt; as a dedicated computation layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are complementary: Cardano provides the trust foundation, while Midnight enables privacy-sensitive applications.&lt;/p&gt;

&lt;h1&gt;
  
  
  Chapter 3: Compact - ZKP Smart Contracts with TypeScript-like Syntax
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
"Aren't zero-knowledge proofs only for cryptography specialists?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Midnight addresses this challenge with a new smart contract language: &lt;strong&gt;Compact&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/midnightntwrk" rel="noopener noreferrer"&gt;
        midnightntwrk
      &lt;/a&gt; / &lt;a href="https://github.com/midnightntwrk/compact" rel="noopener noreferrer"&gt;
        compact
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Compact Releases
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Compact&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Compact is the Midnight Network's smart contract language.
This repository is only used to host Compact releases.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contributing to Compact&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The project welcomes contributions.
If you would like to report a Compact bug, make a feature request, get the source code, etc.
then you should do so at the Minokawa project's &lt;a href="https://github.com/LFDT-Minokawa/compact" rel="noopener noreferrer"&gt;Compact repository&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/midnightntwrk/compact" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Why Compact is exciting
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;TypeScript-based DSL&lt;/strong&gt;: Based on one of the world's most popular languages, allowing web developers to build privacy-focused apps with familiar syntax.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Abstraction of ZK complexity&lt;/strong&gt;: The Compact compiler translates contract logic into the cryptographic material required for proof generation.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Privacy by Design&lt;/strong&gt;: Data is treated as &lt;strong&gt;private by default&lt;/strong&gt;. To expose private data, you must explicitly wrap it with &lt;code&gt;disclose()&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Conceptual Compact example&lt;/span&gt;

&lt;span class="c1"&gt;// Private user vote&lt;/span&gt;
&lt;span class="nx"&gt;witness&lt;/span&gt; &lt;span class="nx"&gt;userVote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Public vote result&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Voting circuit&lt;/span&gt;
&lt;span class="nx"&gt;circuit&lt;/span&gt; &lt;span class="nf"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Update result when validation passes&lt;/span&gt;
  &lt;span class="c1"&gt;// Who voted for what remains private&lt;/span&gt;
  &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// If you want to reveal vote content, do it explicitly&lt;/span&gt;
  &lt;span class="c1"&gt;// disclose(userVote);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Chapter 4: Use Cases Unlocked by Midnight
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Digital ID / KYC&lt;/strong&gt;: Prove you are over 18 without revealing your full birth date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anonymous voting&lt;/strong&gt;: Truly fair voting systems with verified eligibility and ballot secrecy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare&lt;/strong&gt;: Private medical records used for aggregate analysis or AI research.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeFi&lt;/strong&gt;: Access financial services without exposing portfolios or strategies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI and LLMs&lt;/strong&gt;: Use sensitive data for model training while preserving privacy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Summary: The Dawn of a New Privacy Era
&lt;/h1&gt;

&lt;p&gt;Midnight is foundational technology for a safer, fairer digital society where individuals retain data sovereignty and enterprises can innovate responsibly.&lt;/p&gt;

&lt;p&gt;Thank you for reading! 🚀&lt;/p&gt;
&lt;h1&gt;
  
  
  Developer Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://midnight.network/developer-hub" rel="noopener noreferrer"&gt;Midnight Developer Hub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/midnightntwrk" rel="noopener noreferrer"&gt;
        midnightntwrk
      &lt;/a&gt; / &lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps" rel="noopener noreferrer"&gt;
        midnight-awesome-dapps
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Midnight Awesome Dapps
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Awesome Midnight dApps &lt;a href="https://awesome.re" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9d49598b873146ec650fb3f275e8a532c765dabb1f61d5afa25be41e79891aa7/68747470733a2f2f617765736f6d652e72652f62616467652e737667" alt="Awesome"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;This project is built on the Midnight Network.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;A curated list of awesome Midnight dApps, tools, and resources&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Contents&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#awesome-midnight-dapps-" rel="noopener noreferrer"&gt;Awesome Midnight dApps &lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#contents" rel="noopener noreferrer"&gt;Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#smart-contract-primitives" rel="noopener noreferrer"&gt;Smart Contract Primitives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#starter-templates" rel="noopener noreferrer"&gt;Starter Templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#developer-tools" rel="noopener noreferrer"&gt;Developer Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#finance--defi" rel="noopener noreferrer"&gt;Finance &amp;amp; DeFi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#identity--privacy" rel="noopener noreferrer"&gt;Identity &amp;amp; Privacy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#gaming" rel="noopener noreferrer"&gt;Gaming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#governance" rel="noopener noreferrer"&gt;Governance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#dormant-projects" rel="noopener noreferrer"&gt;Dormant Projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#learning-resources" rel="noopener noreferrer"&gt;Learning Resources&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#documentation" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#getting-started-1" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#tutorials" rel="noopener noreferrer"&gt;Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#community" rel="noopener noreferrer"&gt;Community&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#community-projects" rel="noopener noreferrer"&gt;Community Projects&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#contributing" rel="noopener noreferrer"&gt;Contributing&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#submission-criteria" rel="noopener noreferrer"&gt;Submission Criteria&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-awesome-dapps#license" rel="noopener noreferrer"&gt;License&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="markdown-alert markdown-alert-important"&gt;
&lt;p class="markdown-alert-title"&gt;Important&lt;/p&gt;
&lt;p&gt;Community-contributed projects are shared for inspiration and exploration. These repositories are not maintained by the Midnight team, and their functionality may vary.&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-alert markdown-alert-note"&gt;
&lt;p class="markdown-alert-title"&gt;Note&lt;/p&gt;
&lt;p&gt;🔹 = Official Midnight Ecosystem Partner&lt;br&gt;
🏆 = Hackathon winners&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Official dApps and tools maintained by the Midnight team (for education + onboarding)&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/midnightntwrk/example-counter" rel="noopener noreferrer"&gt;Example Counter&lt;/a&gt; - Simple increment/decrement app demonstrating state management&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Olanetsoft/hello-world-compact" rel="noopener noreferrer"&gt;Hello World Compact&lt;/a&gt; - Minimal Hello World smart contract for Midnight. Deploy to Preprod and store/read messages on-chain&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/midnightntwrk/example-bboard" rel="noopener noreferrer"&gt;Example Bboard&lt;/a&gt; - Bulletin board with multi-user interactions and privacy patterns&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/midnightntwrk/example-zkloan" rel="noopener noreferrer"&gt;Example ZK Loan&lt;/a&gt; - ZK-powered…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/midnightntwrk/midnight-awesome-dapps" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/MeshJS" rel="noopener noreferrer"&gt;
        MeshJS
      &lt;/a&gt; / &lt;a href="https://github.com/MeshJS/midnight-starter-template" rel="noopener noreferrer"&gt;
        midnight-starter-template
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Mesh Midnight starter template
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 EDDA - Midnight Starter Template&lt;/h1&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;A starter template for building on Midnight Network with React frontend and smart contract integration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://counter.nebula.builders" rel="nofollow noopener noreferrer"&gt;Live Demo → counter.nebula.builders&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📦 Prerequisites&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; (v23+) &amp;amp; &lt;a href="https://www.npmjs.com/" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt; (v11+)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="nofollow noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-lfs.com/" rel="nofollow noopener noreferrer"&gt;Git LFS&lt;/a&gt; (for large files)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.midnight.network/relnotes/compact-tools" rel="nofollow noopener noreferrer"&gt;Compact&lt;/a&gt; (Midnight developer tools)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chromewebstore.google.com/detail/hgeekaiplokcnmakghbdfbgnlfheichg?utm_source=item-share-cb" rel="nofollow noopener noreferrer"&gt;Lace&lt;/a&gt; (Browser wallet extension)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://faucet.preview.midnight.network/" rel="nofollow noopener noreferrer"&gt;Faucet&lt;/a&gt; (Preview Network Faucet)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Known Issues&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;There’s a not-yet-fixed bug in the arm64 Docker image of the proof server.&lt;/li&gt;
&lt;li&gt;Workaround: Use Bricktower proof server. &lt;strong&gt;bricktowers/proof-server:6.1.0-alpha.6&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🛠️ Setup&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1️⃣ Install Git LFS&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Install and initialize Git LFS&lt;/span&gt;
sudo dnf install git-lfs  &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; For Fedora/RHEL&lt;/span&gt;
git lfs install&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;2️⃣ Install Compact Tools&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Install the latest Compact tools&lt;/span&gt;
curl --proto &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;=https&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; --tlsv1.2 -LsSf \
  https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh &lt;span class="pl-k"&gt;|&lt;/span&gt; sh&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Install the latest compiler&lt;/span&gt;
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Compact compiler version 0.27 should be downloaded manually. Compact tools does not support it currently. &lt;/span&gt;
compact update +0.27.0&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;3️⃣ Install Node.js and docker&lt;/h3&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;Node.js&lt;/a&gt; &amp;amp; &lt;a href="https://www.npmjs.com/" rel="nofollow noopener noreferrer"&gt;npm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="nofollow noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;4️⃣ Verify Installation&lt;/h3&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/MeshJS/midnight-starter-template" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.midnight.network" rel="noopener noreferrer"&gt;Midnight Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network" rel="noopener noreferrer"&gt;Midnight Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>typescript</category>
      <category>zkp</category>
    </item>
    <item>
      <title>Blockchain from the Ground Up: What It Is, Why It Exists, and Where It's Going</title>
      <dc:creator>Gutopro</dc:creator>
      <pubDate>Thu, 09 Apr 2026 20:10:16 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/blockchain-from-the-ground-up-what-it-is-why-it-exists-and-where-its-going-466e</link>
      <guid>https://dev.to/midnight-aliit/blockchain-from-the-ground-up-what-it-is-why-it-exists-and-where-its-going-466e</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%2Fx5b7w873m2kj70kfe8wf.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%2Fx5b7w873m2kj70kfe8wf.jpg" alt="blockchain" width="480" height="320"&gt;&lt;/a&gt;&lt;em&gt;A plain-language breakdown for anyone who's ever been confused by the word "blockchain"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So What Is the Blockchain?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The blockchain is basically a group of computers agreeing upon a single truth. These computers all have copies of this truth, and a single alteration on one changes every copy.&lt;/p&gt;

&lt;p&gt;Think of it like a notebook that thousands of people have identical copies of. Every time something new is written in it, the entries are grouped together into a page — that page is what we call a block.&lt;/p&gt;

&lt;p&gt;Each new entry is mathematically locked to the one before it — like a chain — so you can't go back and alter a page without breaking every page that came after it. And here's the interesting part — no single person owns that notebook. No bank, no government, no tech company. It belongs to everyone and no one at the same time.&lt;/p&gt;

&lt;p&gt;This is what makes it powerful. Because there's no central authority controlling it, nobody can secretly alter the records, freeze your funds, or shut it down. The truth is just... out there, maintained by the network itself.&lt;/p&gt;

&lt;p&gt;That's the blockchain — a shared, tamper-resistant record that nobody owns but everybody can trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Was the Blockchain Created?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The blockchain was created to solve one problem: centralization.&lt;/p&gt;

&lt;p&gt;In the traditional financial system, banks and institutions have total control over every dime in circulation — they can freeze accounts, fail, or act in their own interest. &lt;/p&gt;

&lt;p&gt;In 2008, during the global financial crisis, Satoshi Nakamoto asked: how do we give everybody direct ownership over their assets, without needing to trust a middleman? The answer was decentralization — and that's what blockchain makes possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Blockchain Financial Economy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For centuries, if you wanted to send money, save, invest, or borrow — you needed a middleman. A bank, a broker, an institution. They held the keys to the financial world, and you played by their rules.&lt;br&gt;
The blockchain changed that.&lt;/p&gt;

&lt;p&gt;By creating a system where value can be stored and transferred without a middleman, blockchain opened the door to an entirely new financial economy — one that runs on code instead of institutions.&lt;/p&gt;

&lt;p&gt;At the heart of this economy is cryptocurrency. Just like the naira or the dollar, crypto is money — except no central bank prints it or controls it. It lives on the blockchain, and its rules are written in code that nobody can secretly change.&lt;br&gt;
But it goes deeper than just currency.&lt;/p&gt;

&lt;p&gt;The blockchain financial economy introduced something called DeFi — Decentralized Finance. Think of everything a bank does: savings, loans, investments, payments. DeFi does all of that, but without the bank. Just open protocols that anybody with an internet connection can access, anywhere in the world.&lt;/p&gt;

&lt;p&gt;This matters especially for the billions of people who have been locked out of the traditional financial system — no bank account, no credit history, no access. The blockchain doesn't ask for any of that. If you have a wallet, you're in.&lt;/p&gt;

&lt;p&gt;It's not a perfect system — it has its risks and its chaos. Crypto prices can be wildly volatile — an asset worth a lot today can lose half its value by tomorrow.&lt;/p&gt;

&lt;p&gt;Scams and fraud are rampant because there's no central authority to report to or reverse a bad transaction. If you send money to the wrong address or lose access to your wallet, it's gone. No customer service, no refund.&lt;/p&gt;

&lt;p&gt;DeFi protocols have also been exploited for hundreds of millions of dollars through smart contract bugs and loopholes.&lt;/p&gt;

&lt;p&gt;The code is the law — which is great when it works, and brutal when it doesn't. But for the first time in history, we have a financial economy that doesn't require you to trust anyone. Just the math.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Midnight — The Blockchain Built for the Real World&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The blockchain financial economy opened up a lot of possibilities. But it came with a problem nobody fully solved — privacy.&lt;/p&gt;

&lt;p&gt;Every transaction on most blockchains is public. Anyone can look up your wallet, see your balance, trace your history. For individuals that's uncomfortable. For businesses, it's a deal breaker. You can't run a company on a system where your competitors can see every payment you make.&lt;/p&gt;

&lt;p&gt;That's the problem Midnight was built to solve.&lt;br&gt;
Midnight is a blockchain that puts privacy at its core — not as an afterthought, but as the foundation. It's built by Input Output, the same team behind Cardano, and it introduces a new idea: you should be able to prove something is true without revealing everything behind it.&lt;/p&gt;

&lt;p&gt;Imagine proving you're old enough to buy something without showing your date of birth. Or proving you have enough funds for a transaction without exposing your full balance. That's the kind of thing Midnight makes possible, through a technology called zero-knowledge proofs.&lt;br&gt;
But Midnight doesn't throw away accountability entirely.&lt;/p&gt;

&lt;p&gt;It's designed to balance privacy with compliance — so individuals and businesses can protect their sensitive data while still operating within legal and regulatory boundaries.&lt;/p&gt;

&lt;p&gt;This is what makes Midnight different. Not just another blockchain, but an attempt to build the infrastructure for a world where people and businesses can participate in the decentralized economy on their own terms — without sacrificing privacy to do it.&lt;/p&gt;

&lt;p&gt;The blockchain gave us financial freedom. Midnight is trying to give us financial freedom with dignity.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading. If this series helped you understand blockchain a little better, follow along — there's more coming.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>midnightchallenge</category>
      <category>aliit</category>
      <category>web3</category>
    </item>
    <item>
      <title>I Spent Hours in the DOM So You Don't Have To</title>
      <dc:creator>Tushar Pamnani</dc:creator>
      <pubDate>Wed, 08 Apr 2026 20:57:19 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/i-spent-hours-in-the-dom-so-you-dont-have-to-e8h</link>
      <guid>https://dev.to/midnight-aliit/i-spent-hours-in-the-dom-so-you-dont-have-to-e8h</guid>
      <description>&lt;p&gt;&lt;em&gt;Full source: &lt;a href="https://github.com/tusharpamnani/midnight-wallet-kit" rel="noopener noreferrer"&gt;github.com/tusharpamnani/midnight-wallet-kit&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I started building frontends on Midnight, I hit a wall that nobody warned me about.&lt;/p&gt;

&lt;p&gt;The contracts were working. The proof server was running. The TypeScript SDK was integrated. And then I needed to connect a wallet.&lt;/p&gt;

&lt;p&gt;There was no proper documentation for Lace's Midnight provider. No fixed &lt;code&gt;window.ethereum&lt;/code&gt;-style standard to follow. Just two browser extensions injecting objects into the DOM under different keys, with different method signatures, inconsistent behavior, and no specification to read.&lt;/p&gt;

&lt;p&gt;I spent hours inspecting &lt;code&gt;window&lt;/code&gt; objects, console-logging provider payloads, and reverse-engineering what each wallet actually exposed before I could make a single connection work reliably. Every Midnight frontend developer hits this exact wall. Nobody should have to climb it twice.&lt;/p&gt;

&lt;p&gt;That's why I built &lt;code&gt;midnight-wallet-kit&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Problem Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;If you've built on Ethereum, you know &lt;code&gt;window.ethereum&lt;/code&gt;. It's a standard. You call &lt;code&gt;eth_requestAccounts&lt;/code&gt;, you get addresses, you sign. Every wallet implements the same interface.&lt;/p&gt;

&lt;p&gt;Midnight doesn't have this yet. Lace injects under &lt;code&gt;window.lace&lt;/code&gt;. 1AM injects under &lt;code&gt;window.midnight&lt;/code&gt;. The methods exist but aren't documented. The payload shapes vary. And because Midnight uses ZK proofs and shielded transactions, the signing flow is more complex than EVM; you're not just signing a hash, you're signing an &lt;em&gt;intent&lt;/em&gt; that the proof server will process.&lt;/p&gt;

&lt;p&gt;Without a proper abstraction layer, every developer ends up writing the same fragile, bespoke integration code:&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="c1"&gt;// What you end up writing without a kit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;lace&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;midnight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lace not found?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
&lt;span class="c1"&gt;// hope this works, documentation doesn't exist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you do the same thing for 1AM. Then you handle the case where neither is installed. Then you handle session persistence across page refreshes. Then you write tests that require a real browser extension to be installed. By the time you're done, you've written a wallet library, except it only works for your specific app and breaks when the extension updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Midnight Wallet Kit Gives You
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;midnight-wallet-kit&lt;/code&gt; is a production-grade abstraction layer for Lace and 1AM on Midnight. Install it, register your adapters, wrap your app in a provider, and you're done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;midnight-wallet-kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WalletManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InjectedWalletAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;midnight-wallet-kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WalletManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;manager&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InjectedWalletAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;providerKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InjectedWalletAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1AM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;providerKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;midnight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WalletProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;midnight-wallet-kit/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WalletProvider&lt;/span&gt;
      &lt;span class="na"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;autoConnect&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1AM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// priority fallback order&lt;/span&gt;
      &lt;span class="na"&gt;autoRestore&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;              &lt;span class="c1"&gt;// reconnect last-used wallet on refresh&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;WalletProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;That's the entire integration. Everything below this point is application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Hooks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;useWallet()&lt;/code&gt;: connection state
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connectionState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;connectionState&lt;/code&gt; gives you the full lifecycle: &lt;code&gt;idle → connecting → connected&lt;/code&gt;, with &lt;code&gt;restoring&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;disconnecting&lt;/code&gt;, and &lt;code&gt;disconnected&lt;/code&gt; as intermediate states. No more boolean &lt;code&gt;isConnected&lt;/code&gt; that doesn't tell you &lt;em&gt;why&lt;/em&gt; a connection failed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;address&lt;/code&gt; returns the user's Midnight unshielded address — the same &lt;code&gt;mn_addr_...&lt;/code&gt; format used throughout the contract layer. &lt;code&gt;coinPublicKey&lt;/code&gt; and &lt;code&gt;encryptionPublicKey&lt;/code&gt; are also available if your dApp needs them.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;useConnect()&lt;/code&gt;: connection management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;adapters&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useConnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Connect to a specific wallet&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1AM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Or let the kit try in priority order&lt;/span&gt;
&lt;span class="c1"&gt;// (handled automatically via WalletProvider autoConnect)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;adapters&lt;/code&gt; gives you the list of all registered adapter names; useful for rendering a wallet selection UI without hardcoding names.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;useIntent()&lt;/code&gt;: signing
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;buildAndSign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useIntent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Sign a contract intent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildAndSign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;circuitId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxCost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Sign an arbitrary message (login flows, proofs of ownership)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login to My DApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Timestamping and normalization handled automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;buildAndSign&lt;/code&gt; validates intent parameters with Zod before sending anything to the wallet; you get a typed &lt;code&gt;InvalidIntentError&lt;/code&gt; before the extension even opens, not a cryptic failure deep in the proof pipeline.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;signMessage&lt;/code&gt; handles the multi-step probing for data-signing support across different wallet versions, adds proper prefixes, and generates unique timestamps automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;useBalance()&lt;/code&gt;: balance polling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useBalance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// balance: { tDUST: bigint; shielded: bigint } | null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automatically polls every 15 seconds when connected. &lt;code&gt;refetch()&lt;/code&gt; is available for manual triggers after a transaction. Uses the Midnight indexer under the hood; if the indexer query fails, you get a typed &lt;code&gt;BalanceFetchError&lt;/code&gt;, not a silent &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: Why It's Built This Way
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adapters normalize the provider chaos
&lt;/h3&gt;

&lt;p&gt;Every wallet integration lives in an adapter that implements &lt;code&gt;MidnightWallet&lt;/code&gt;. The &lt;code&gt;InjectedWalletAdapter&lt;/code&gt; handles the DOM-level provider probing, the part I spent hours figuring out manually. It exhaustively searches for working RPC methods across different provider standards and payload formats, so if Lace updates their injection key or 1AM changes their method signature, you update one adapter, not every component in your app.&lt;/p&gt;

&lt;p&gt;Wallet modes are explicitly typed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;intent-signing&lt;/code&gt;: supports the full &lt;code&gt;signIntent()&lt;/code&gt; flow, standard for DApps&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tx-only&lt;/code&gt;: direct transaction balancing and submission only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unknown&lt;/code&gt;: handled defensively as a fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WalletManager handles the lifecycle
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;WalletManager&lt;/code&gt; is the orchestrator. It manages adapter registration, connection state transitions, fallback chains, middleware, and session persistence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fallback chains&lt;/strong&gt; are the feature I wish I'd had from day one:&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;await&lt;/span&gt; &lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectWithFallback&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1AM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try 1AM first. If it's not installed or the user rejects, try Lace. If both fail, throw &lt;code&gt;FallbackExhaustedError&lt;/code&gt;. One line. No manual try-catch chains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Session persistence&lt;/strong&gt; via &lt;code&gt;autoRestore&lt;/code&gt; stores the last-connected wallet name in &lt;code&gt;localStorage&lt;/code&gt; and attempts to reconnect on page load. Users stay connected across refreshes without re-approving the extension every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Middleware for observability
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Starting &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; on &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adapterName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wallet_error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every wallet operation; connect, disconnect, signIntent, signMessage - passes through the middleware chain. The context object gives you the operation type, adapter name, intent payload, result, and any error. Useful for logging, analytics, and debugging production issues without adding instrumentation to every component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typed Errors: No More &lt;code&gt;catch (e: any)&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Every error from the kit is a typed class inheriting from &lt;code&gt;MidnightWalletError&lt;/code&gt;. You can branch on error type rather than parsing message strings:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;ProviderNotFoundError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ConnectionRejectedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;FallbackExhaustedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;NetworkMismatchError&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;midnight-wallet-kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ProviderNotFoundError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showInstallPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ConnectionRejectedError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showRejectedMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;NetworkMismatchError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showNetworkSwitchPrompt&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;NetworkMismatchError&lt;/code&gt; in particular is one you'll hit in the wild, if a user switches their wallet to mainnet while your dApp is pointed at preprod, the session breaks silently without this check. The kit detects and surfaces it explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Without a Browser Extension
&lt;/h2&gt;

&lt;p&gt;This is the part that usually breaks dApp test suites. Testing wallet integration normally requires a real browser extension to be installed and configured, which makes CI impossible.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MockWalletAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;midnight-wallet-kit/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockWalletAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TestWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mn_addr1_test...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;coinPublicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test_cpk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;signatureOverride&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0xmocksignature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;signDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// simulate realistic latency&lt;/span&gt;
  &lt;span class="na"&gt;shouldRejectSign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can simulate connection failures, signing rejections, latency, and specific return values. No browser, no extension, no environment setup; just a mock that implements the same &lt;code&gt;MidnightWallet&lt;/code&gt; interface as the real adapters.&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="c1"&gt;// Test the rejection flow&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;failAdapter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MockWalletAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RejectingWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shouldRejectConnect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RejectingWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ConnectionRejectedError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SSR and Next.js
&lt;/h2&gt;

&lt;p&gt;While building Midnight Club in Next.js, &lt;code&gt;window.lace&lt;/code&gt; and &lt;code&gt;window.midnight&lt;/code&gt; were being accessed during server-side rendering, causing &lt;code&gt;window is not defined&lt;/code&gt; crashes that were silent in development but broke production builds.&lt;/p&gt;

&lt;p&gt;The React hooks are SSR-safe. Provider detection (&lt;code&gt;window.lace&lt;/code&gt;, &lt;code&gt;window.midnight&lt;/code&gt;) only runs on the client; no &lt;code&gt;window is not defined&lt;/code&gt; errors during Next.js server-side rendering. &lt;code&gt;WalletProvider&lt;/code&gt; guards all DOM access behind a mounted check, so your app hydrates cleanly without injecting wallet state during SSR.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The kit currently supports Lace and 1AM via &lt;code&gt;InjectedWalletAdapter&lt;/code&gt;. Hardware wallet support is the natural next step as the Midnight ecosystem matures and physical signing devices become available.&lt;/p&gt;

&lt;p&gt;If you're building on Midnight and hit something the kit doesn't handle, an edge case in the provider, a new wallet, a signing flow that doesn't fit the current API - issues and PRs are open at &lt;a href="https://github.com/tusharpamnani/midnight-wallet-kit" rel="noopener noreferrer"&gt;github.com/tusharpamnani/midnight-wallet-kit&lt;/a&gt;&lt;/p&gt;

</description>
      <category>midnight</category>
      <category>react</category>
      <category>typescript</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Troubleshooting Midnight Pre-Prod: A Week in the Trenches</title>
      <dc:creator>Gutopro</dc:creator>
      <pubDate>Wed, 08 Apr 2026 08:30:29 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/troubleshooting-midnight-pre-prod-a-week-in-the-trenches-2g4d</link>
      <guid>https://dev.to/midnight-aliit/troubleshooting-midnight-pre-prod-a-week-in-the-trenches-2g4d</guid>
      <description>&lt;p&gt;Building on Midnight feels like being on the frontier of Web3. Between the Zero-Knowledge (ZK) primitives and the unique dual-token model, there is a lot to love—but being an early adopter means running into some undocumented "features."&lt;/p&gt;

&lt;p&gt;I spent this week building on the Midnight pre-prod network from scratch. While the documentation is improving, I encountered several blockers that weren't immediately obvious. I also spent a lot of time in the Discord monitoring common pitfalls other devs are hitting.&lt;/p&gt;

&lt;p&gt;If you are setting up your Midnight environment today, bookmark this. Here is how to fix the most common environment issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The "Invalid Address" Faucet Trap&lt;/strong&gt;&lt;br&gt;
The Issue: You try to fund your wallet using the Lace faucet, but it returns an "Address provided was invalid" error, even after you confirm you are on the pre-prod network.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Fix&lt;/em&gt;: Switch to the 1AM wallet.&lt;/p&gt;

&lt;p&gt;Crucial Step: You must set the wallet to the pre-prod network BEFORE you copy your address. A mainnet address string—even if generated by the same seed—will not be recognized by the pre-prod faucet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set network to preprod&lt;/span&gt;
&lt;span class="nf"&gt;setNetworkId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preprod&lt;/span&gt;&lt;span class="dl"&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;2. Deployment Failures (deploy.ts)&lt;/strong&gt;&lt;br&gt;
The Issue: Your deploy.ts script fails to create or connect to a wallet. This usually happens because the default configuration in many boilerplate templates doesn't align with the 1AM wallet's requirements.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Fix&lt;/em&gt;: This is a two-step process that is currently undocumented:&lt;/p&gt;

&lt;p&gt;Replace your pre-prod configuration file with the specific 1AM network config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Preprod network configuration&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;indexer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://indexer.preprod.midnight.network/api/v4/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;indexerWS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://indexer.preprod.midnight.network/api/v4/graphql/ws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.preprod.midnight.network&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;proofServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://127.0.0.1:6300&lt;/span&gt;&lt;span class="dl"&gt;'&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;Use the CLI to create your wallet after you have integrated that new config.&lt;br&gt;
Doing only one of these will result in a hang. You need both to bridge the gap between the script and the network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. DUST Registration Timing Out&lt;/strong&gt;&lt;br&gt;
The Issue: You attempt to register for DUST, but the process hangs or fails repeatedly.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Fix&lt;/em&gt;: This shares the same root cause as the deployment issue. If your config is pointing even slightly off-target, the registration handshake won't complete. Ensure you are using the 1AM network configuration before you even attempt the registration command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The "Ghost DUST" Bug&lt;/strong&gt;&lt;br&gt;
The Issue: Several devs in Discord reported getting a green "Success" checkmark on the faucet, but no tDUST arrives, even after 48 hours.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Fix&lt;/em&gt;: Currently, the most reliable workaround is to use the 1AM wallet specifically. In most cases, DUST credits immediately there, whereas Lace is seeing intermittent sync issues with the faucet during this pre-prod phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Unexpected Gas Drainage&lt;/strong&gt;&lt;br&gt;
The Issue: You’re building, and suddenly everything stops working because your tDUST balance is zero.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Fix&lt;/em&gt;: Remember that every write operation consumes tDUST as gas. Because we are in a ZK environment, these costs can add up during heavy testing.&lt;/p&gt;

&lt;p&gt;Pro-Tip: Use the 1AM Explorer to track your transaction history and gas per interaction. Refuel from the faucet proactively rather than waiting for a failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Contract Interaction "De-sync"&lt;/strong&gt;&lt;br&gt;
The Issue: Your contract interactions work for a while, then suddenly start failing mid-session without code changes.&lt;br&gt;
_&lt;br&gt;
The Fix_: The wallet (specifically Lace) sometimes loses sync with the chain or suffers an authentication timeout.&lt;/p&gt;

&lt;p&gt;The Workflow: Manually click Sync in your wallet before every contract interaction. It feels redundant, but it prevents the "ghost failures" that catch most devs off guard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My "Golden" Setup&lt;/strong&gt;&lt;br&gt;
If you want a stable environment to actually write code, here is the stack I currently have running successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime: Node 22&lt;/li&gt;
&lt;li&gt;Compiler: Latest Compact compiler&lt;/li&gt;
&lt;li&gt;Wallet: 1AM wallet (on pre-prod)&lt;/li&gt;
&lt;li&gt;Workflow: Wallet created via CLI using 1AM config&lt;/li&gt;
&lt;li&gt;Infrastructure: Proof server running locally via Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verification: Contracts deployed and verified via 1AM Explorer&lt;/p&gt;

&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%2Ferdnhl6ymb2q5bluzs3e.png" 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%2Ferdnhl6ymb2q5bluzs3e.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&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%2Fvm395awebh21ebzze3ca.png" 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%2Fvm395awebh21ebzze3ca.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s Next?&lt;/strong&gt;&lt;br&gt;
I’m currently working through Midnight 301 (Advanced ZK DApp Development), building a privacy-preserving bulletin board. I’ll be documenting the full build-out as I go.&lt;/p&gt;

&lt;p&gt;If you're stuck on a specific error, drop a comment or find me in the Midnight Discord. Let's build.&lt;/p&gt;

&lt;h1&gt;
  
  
  midnight #zkproofs #blockchain #web3 #tutorial
&lt;/h1&gt;

</description>
      <category>midnightchallenge</category>
      <category>web3</category>
      <category>aliit</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Zero-Knowledge, Zero Friction: Automating DApp Development on Midnight</title>
      <dc:creator>Nasihudeen Jimoh</dc:creator>
      <pubDate>Tue, 07 Apr 2026 22:30:27 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/zero-knowledge-zero-friction-automating-dapp-development-on-midnight-29f1</link>
      <guid>https://dev.to/midnight-aliit/zero-knowledge-zero-friction-automating-dapp-development-on-midnight-29f1</guid>
      <description>&lt;p&gt;This article provides a step by step deep dive into &lt;a href="https://github.com/Kanasjnr/midnight-pulse-sdk-demo" rel="noopener noreferrer"&gt;&lt;strong&gt;Midnight Pulse&lt;/strong&gt;&lt;/a&gt;, a collaborative analytics tool built to showcase the power of the &lt;a href="https://www.npmjs.com/package/midnight-sdk-gen" rel="noopener noreferrer"&gt;&lt;strong&gt;Midnight SDK Generator&lt;/strong&gt;&lt;/a&gt;. We'll walk through the entire lifecycle: from defining a privacy preserving smart contract in Compact to building a multi user CLI application in TypeScript.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Vision: Privacy at Scale
&lt;/h2&gt;

&lt;p&gt;The goal of Midnight Pulse is to allow a team to compute a "salary pulse" a benchmark average without any individual ever revealing their sensitive data to anyone else (not even a central server).&lt;/p&gt;

&lt;p&gt;We solve this using &lt;strong&gt;Zero-Knowledge (ZK) proofs&lt;/strong&gt; and a strict &lt;strong&gt;Anonymity Threshold&lt;/strong&gt; ($N \ge 5$). The logic is simple: the application won't let you see the results until at least 5 people have joined.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: The "Glue Code" Friction
&lt;/h2&gt;

&lt;p&gt;Developing on Midnight involves interacting with ZK-circuits and a private ledger. Manually writing the TypeScript glue code to call these circuits and read ledger state is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error-prone&lt;/strong&gt;: Manual type mapping can lead to runtime failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance-heavy&lt;/strong&gt;: Every contract change requires a manual update to the SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate-intensive&lt;/strong&gt;: Setting up contract stubs and providers involves repetitive code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Midnight SDK Generator&lt;/strong&gt; solves this by deriving a production-ready SDK directly from your contract's metadata.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Privacy Contract (&lt;code&gt;salary.compact&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Everything starts with the contract. Using &lt;strong&gt;Compact&lt;/strong&gt;, we define what data is public and what logic is private.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ledger
&lt;/h3&gt;

&lt;p&gt;We store the aggregate metrics (Sum and Headcount) on the &lt;strong&gt;Shielded Ledger&lt;/strong&gt;. This means the data is encrypted on-chain, and only the contract logic can update it.&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;export&lt;/span&gt; &lt;span class="n"&gt;ledger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;total_salary_sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Uint64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;employee_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Uint64&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Submit" Circuit
&lt;/h3&gt;

&lt;p&gt;When a user submits their salary, they don't send the value in the clear. Instead, they run a &lt;strong&gt;circuit&lt;/strong&gt; that privately adds their value to the aggregate.&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 submit_salary(salary: Uint64): [] {
  const current_sum = ledger.total_salary_sum;
  const current_count = ledger.employee_count;

  ledger.total_salary_sum = current_sum + salary;
  ledger.employee_count = current_count + 1;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Benchmark" Circuit (Anonymity Check)
&lt;/h3&gt;

&lt;p&gt;This is where the privacy guarantee is enforced. The circuit checks the &lt;code&gt;employee_count&lt;/code&gt; before performing the comparison.&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 is_above_benchmark(my_salary: Uint64): Boolean {
  const count = ledger.employee_count;
  const total = ledger.total_salary_sum;

  // The Privacy Gate: Revert if less than 5 people have joined
  check(count &amp;gt;= 5) "Threshold Error: N &amp;lt; 5 contributors";

  const average = total / count;
  return my_salary &amp;gt; average;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Generating the SDK
&lt;/h2&gt;

&lt;p&gt;Once the contract is written, we use the SDK Generator to bridge the gap between Compact and TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Compile to Metadata
&lt;/h3&gt;

&lt;p&gt;First, use the &lt;code&gt;compact&lt;/code&gt; compiler to generate a structured JSON representation of your contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact compile ./contracts/salary.compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Generate the SDK
&lt;/h3&gt;

&lt;p&gt;Once you have your &lt;code&gt;salary.structure.json&lt;/code&gt;, use the generator to create your type-safe SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;midnight-sdk-gen ./contracts/salary.structure.json &lt;span class="nt"&gt;--output&lt;/span&gt; ./src/sdk/SalarySDK.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Type Mapping Support
&lt;/h3&gt;

&lt;p&gt;The generator automatically maps Compact types to their most appropriate TypeScript equivalents, so you never have to guess:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Compact Type&lt;/th&gt;
&lt;th&gt;TypeScript Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Uint&amp;lt;N&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bigint&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Bytes&amp;lt;N&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Uint8Array&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Maybe&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;`T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;{% raw %}&lt;code&gt;Vector&amp;lt;N, T&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;T[]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Application Layer (&lt;code&gt;pulse.ts&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;With the generated SDK, building the CLI tool is straightforward. We use an &lt;strong&gt;Agentic Pattern&lt;/strong&gt; to simulate multiple participants.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Initializing Providers
&lt;/h3&gt;

&lt;p&gt;We first set up the Midnight network context (Wallet, Proof Server, Indexer). Our &lt;code&gt;providers.ts&lt;/code&gt; bridge allows us to toggle between &lt;strong&gt;Fast Simulation&lt;/strong&gt; and the &lt;strong&gt;Local Docker Network&lt;/strong&gt;.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MIDNIGHT_ENV&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;simulated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProviders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Deploying the Contract
&lt;/h3&gt;

&lt;p&gt;The team leader (or a smart contract factory) deploys the instance using the generated &lt;code&gt;deploy&lt;/code&gt; method.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SalarySDK&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./sdk/SalarySDK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SalarySDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contractAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Orchestrating the Users
&lt;/h3&gt;

&lt;p&gt;We loop through our simulated agents (Alice, Bob, Carol, Dave, Eve). Each agent joins the contract and submits their salary.&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Join the same shared contract address using the generated SDK&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agentSdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SalarySDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Submit salary privately via the ZK circuit&lt;/span&gt;
  &lt;span class="c1"&gt;// The SDK handles all witness and proof management&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agentSdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit_salary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;witness&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Log progress using side-by-side observability&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agentSdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicDataProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryContractState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;StateObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayPulse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicState&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;h3&gt;
  
  
  4. Running the Benchmark
&lt;/h3&gt;

&lt;p&gt;Finally, Carol checks if she is above the average. Because all 5 agents have now submitted, the $N \ge 5$ check in the circuit passes successfully.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAbove&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;carolSdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_above_benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;carol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;witness&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;StateObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Carol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAbove&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Observability (The Pulse)
&lt;/h2&gt;

&lt;p&gt;One of the biggest challenges in ZK development is &lt;strong&gt;"blind debugging."&lt;/strong&gt; To solve this, i built a &lt;code&gt;StateObserver&lt;/code&gt; to visualize the delta of privacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Side-by-Side Verification
&lt;/h3&gt;

&lt;p&gt;The terminal output provides a side-by-side view. As you can see below, the &lt;strong&gt;Public State&lt;/strong&gt; tracks the group metrics, while the &lt;strong&gt;Private State&lt;/strong&gt; remains strictly isolated in the user's local witness.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- [Alice] Status ---

    ┌──────────────────────────────┬──────────────────────────────┐
    │ PUBLIC STATE               │ PRIVATE STATE              │
    ├──────────────────────────────┼──────────────────────────────┤
    │ Total Sum:   92,000          │ My Salary:   92,000          │
    │ Headcount:   1               │ ZK-Proof:    VALID          │
    └──────────────────────────────┴──────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the developer to verify that privacy is actually being maintained—Alice can see the total sum grow, but she never sees Bob's individual contribution.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: The New Way to Build ZK
&lt;/h2&gt;

&lt;p&gt;Midnight Pulse proves that building ZK applications doesn't have to be hard. By using &lt;strong&gt;Compact&lt;/strong&gt; for privacy logic and the &lt;strong&gt;SDK Generator&lt;/strong&gt; for the application layer, we can build sophisticated, privacy preserving systems with standard TypeScript skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Maintenance&lt;/strong&gt;: Changing your contract automatically updates your SDK types.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt;: No more runtime errors from incorrect type mapping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Velocity&lt;/strong&gt;: Focus on your dApp logic, not the cryptographic plumbing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  See the Pulse in 60 seconds
&lt;/h3&gt;

&lt;p&gt;Clone the repository and run the multi-agent simulation on your own machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Kanasjnr/midnight-pulse-sdk-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;midnight-pulse-sdk-demo
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>opensource</category>
      <category>blockchain</category>
      <category>security</category>
    </item>
    <item>
      <title>Building a Fungible Token in Compact (Midnight)</title>
      <dc:creator>Neeraj Choubisa</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:35:07 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/building-a-fungible-token-in-compact-midnight-3nfh</link>
      <guid>https://dev.to/midnight-aliit/building-a-fungible-token-in-compact-midnight-3nfh</guid>
      <description>&lt;p&gt;The rise of privacy-focused blockchains like Midnight is introducing developers to a new paradigm of smart contract development using Compact language.&lt;/p&gt;

&lt;p&gt;If you are coming from Solidity, things may feel unfamiliar at first, especially concepts like inheritance, storage, and function definitions.&lt;/p&gt;

&lt;p&gt;In this article, you will learn how fungible tokens work in Compact, why inheritance does not exist, how to build your own token step by step, and how to fix common errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Compact
&lt;/h2&gt;

&lt;p&gt;Compact does not support inheritance like Solidity.&lt;/p&gt;

&lt;p&gt;Instead of writing:&lt;br&gt;
contract MyToken is ERC20&lt;/p&gt;

&lt;p&gt;Compact uses modules and composition. This means you import functionality and explicitly use it. There is no hidden behavior.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the FungibleToken Module
&lt;/h2&gt;

&lt;p&gt;The FungibleToken module provides core token functionality such as balances tracking, allowances, transfers, minting and burning, and metadata like name, symbol, and decimals.&lt;/p&gt;

&lt;p&gt;There are a few differences compared to ERC20:&lt;br&gt;
It uses Uint&amp;lt;128&amp;gt; instead of uint256&lt;br&gt;
There are no events&lt;br&gt;
Contract to contract transfers are not supported yet&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Import the Module
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "./token/FungibleToken.compact" prefix FT_;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 2: Create Your Token Contract
&lt;/h2&gt;


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

import "./token/FungibleToken.compact" prefix FT_;

contract CustomFungibleToken {

  circuit init(): [] {
    FT_.initialize("Nikku Token", "NIK", 18);

    const owner = left&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(ownPublicKey());
    FT_._mint(owner, 1000000);
  }

  circuit transfer(
    to: Either&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;,
    amount: Uint&amp;lt;128&amp;gt;
  ): Boolean {
    return FT_.transfer(to, amount);
  }

  circuit balanceOf(
    account: Either&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;
  ): Uint&amp;lt;128&amp;gt; {
    return FT_.balanceOf(account);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Compile the Contract
&lt;/h2&gt;

&lt;p&gt;Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Concepts to Remember
&lt;/h2&gt;

&lt;p&gt;Compact does not support inheritance.&lt;br&gt;
You must use circuit instead of function definitions.&lt;br&gt;
Modules manage their own storage.&lt;br&gt;
Initialization is required and must not be skipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Limitations
&lt;/h2&gt;

&lt;p&gt;Contract to contract calls are not supported.&lt;br&gt;
Events are not available.&lt;br&gt;
Only 128 bit integers are supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental Model
&lt;/h2&gt;

&lt;p&gt;Instead of thinking that your contract extends ERC20, think of it as using a token module and calling its functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build Next
&lt;/h2&gt;

&lt;p&gt;You can extend this basic token to add mint restrictions, implement fees, or build more advanced applications on Midnight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Compact encourages explicit and predictable contract design. While it may take some time to adjust, it leads to better security and modularity.&lt;/p&gt;

&lt;p&gt;Once you understand this pattern, building on Midnight becomes much easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  About Author ?
&lt;/h2&gt;

&lt;p&gt;Hi, I’m Nikku.Dev, a Full Stack Blockchain Developer and builder. I work on smart contracts, DeFi systems, and Web3 applications, focusing on building real, scalable products.&lt;/p&gt;

&lt;p&gt;Portfolio: &lt;a href="https://nikkudotdev.vercel.app/" rel="noopener noreferrer"&gt;https://nikkudotdev.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/Kali-Decoder" rel="noopener noreferrer"&gt;https://github.com/Kali-Decoder&lt;/a&gt;&lt;/p&gt;

</description>
      <category>midnightchallenge</category>
      <category>compact</category>
      <category>blockchain</category>
      <category>privacy</category>
    </item>
    <item>
      <title>Advanced Compact Patterns for Web3 Developers</title>
      <dc:creator>Nasihudeen Jimoh</dc:creator>
      <pubDate>Thu, 02 Apr 2026 20:08:39 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/advanced-compact-patterns-for-web3-developers-4m9i</link>
      <guid>https://dev.to/midnight-aliit/advanced-compact-patterns-for-web3-developers-4m9i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you've spent years building on EVM chains, Midnight's architecture might feel like a paradigm shift. On Ethereum, you push computation onto the blockchain itself. On Midnight, you do the opposite you move computation off-chain and prove it correctly using zero-knowledge proofs.&lt;/p&gt;

&lt;p&gt;This isn't just a different implementation detail. It fundamentally changes how you think about state management, data disclosure, and circuit design.&lt;/p&gt;

&lt;p&gt;Samantha's &lt;a href="https://dev.tolink-to-samantha-post"&gt;foundational guide&lt;/a&gt; introduced the three-part structure of Midnight contracts: the public ledger, zero-knowledge circuits, and local computation. But understanding the basics and architecting production systems are two different challenges.&lt;/p&gt;

&lt;p&gt;This guide dives into the patterns that separate working prototypes from robust systems. We'll explore how witnesses enable privacy boundaries, why commitments matter more than direct state, how to optimize circuits for real-world constraints, and how to compose multiple private contracts without leaking metadata.&lt;/p&gt;

&lt;p&gt;By the end, you'll have concrete strategies for building systems that maintain privacy guarantees while managing the practical tradeoffs of Web3 applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Witnesses &amp;amp; Selective Disclosure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Understanding Witnesses Beyond Function Calls
&lt;/h3&gt;

&lt;p&gt;In EVM contracts, all data available to a function is deterministic. The blockchain is your single source of truth. In Compact, witnesses invert this model: witnesses are the &lt;em&gt;only&lt;/em&gt; source of truth the contract doesn't control.&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="c1"&gt;// A witness declares a contract's dependency on external data&lt;/span&gt;
&lt;span class="nx"&gt;witness&lt;/span&gt; &lt;span class="nf"&gt;getUserSecret&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;witness&lt;/span&gt; &lt;span class="nf"&gt;getProofOfAssets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Uint&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;AssetsProof&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you declare a witness, you're saying: "This contract's logic depends on data I cannot verify on-chain. It's the application's responsibility to provide this correctly."&lt;/p&gt;

&lt;p&gt;This creates a critical security boundary. The contract trusts the application to supply honest witnesses, but the proof system validates that the application used those witnesses correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Witness-Disclosure Loop
&lt;/h3&gt;

&lt;p&gt;Real-world contracts don't just consume witnesses they combine witness data with disclosed state to create privacy preserving outcomes.&lt;/p&gt;

&lt;p&gt;Consider an age verification system:&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;

// Public ledger: only record that someone proved they're eligible
export ledger ageVerified: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Boolean&amp;gt;;
export ledger verificationRound: Counter;

// Private witness: the user's actual birthdate (never on-chain)
witness getUserBirthDate(): Uint&amp;lt;32&amp;gt;; // Unix timestamp

// Derived public key with round counter to prevent replay
circuit derivePublicIdentity(round: Field, secret: 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;(
    [round as Bytes&amp;lt;32&amp;gt;, secret]
  );
}

// Main circuit: prove age without revealing birthdate
export circuit verifyAge(secret: Bytes&amp;lt;32&amp;gt;, minAge: Uint&amp;lt;32&amp;gt;): [] {
  // Get private birthdate (witness - not on-chain)
  const birthDate = getUserBirthDate();

  // Compute age
  const currentTimestamp: Uint&amp;lt;32&amp;gt; = 1704067200; // Updated by app
  const age = currentTimestamp - birthDate;

  // Private check: verify age requirement
  assert(age &amp;gt;= minAge, "Age requirement not met");

  // Public disclosure: only record that verification happened
  const identity = derivePublicIdentity(verificationRound.roundNumber, secret);
  ageVerified = disclose(identity, true);
  verificationRound.increment(1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key pattern&lt;/strong&gt;: The witness data (&lt;code&gt;getUserBirthDate&lt;/code&gt;) is never directly disclosed. Instead, you compute a predicate over it (&lt;code&gt;age &amp;gt;= minAge&lt;/code&gt;), and then disclose only the outcome the user consents to.&lt;/p&gt;

&lt;p&gt;The tradeoff: The application code that supplies the witness must be trusted. If a malicious DApp sends a false birthdate, the proof system can't detect it—but it will prove the user accepted false data. This is why witness sourcing matters as much as circuit logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Consideration: Witness Sourcing
&lt;/h3&gt;

&lt;p&gt;Where do witnesses come from in real applications?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User-held secrets&lt;/strong&gt;: API keys, private keys, personal data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External APIs&lt;/strong&gt;: Proof-of-reserve attestations, oracle feeds, credential issuers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-knowledge proofs themselves&lt;/strong&gt;: A sub-proof generated off-chain that proves something about external data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trusted hardware&lt;/strong&gt;: TEE attestations or trusted execution environment outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each source has different security properties. A witness from a user's secret key is as strong as that key's protection. A witness from an untrusted API might need cryptographic verification itself.&lt;/p&gt;

&lt;p&gt;For example, if your witness comes from an API like "get current asset price," the application must either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trust the API (weak)&lt;/li&gt;
&lt;li&gt;Verify the API response against multiple oracles (medium)&lt;/li&gt;
&lt;li&gt;Require the API to provide a signature from a trusted source (better)&lt;/li&gt;
&lt;li&gt;Use sub-proofs to prove the API data meets certain criteria without revealing it (best)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Commitments &amp;amp; Zero-Knowledge Proof Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From State Mutation to Commitment Schemes
&lt;/h3&gt;

&lt;p&gt;EVM developers are accustomed to direct state mutations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Ethereum: modify state directly
mapping(address =&amp;gt; uint256) balance;
balance[user] += amount;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Midnight, public state mutations must be &lt;em&gt;proven&lt;/em&gt; by a zero-knowledge circuit. This means your public state must be designed around commitment schemes cryptographic structures that let you prove you know a value without revealing it.&lt;/p&gt;

&lt;p&gt;Here's the conceptual bridge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EVM thinking&lt;/strong&gt;: State is a mutable cell. Update it directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Midnight thinking&lt;/strong&gt;: State is a commitment to a value. Prove you know the value, then update the commitment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building a Private Ledger with Commitments
&lt;/h3&gt;

&lt;p&gt;Let's walk through a private token transfer system:&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;

// Public state: only commitments to balances, never actual amounts
export ledger balanceCommitment: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Field&amp;gt;;
export ledger totalSupply: Uint&amp;lt;128&amp;gt;;
export ledger transferRound: Counter;

// Private data structure (never on-chain, only proven)
struct Account {
  owner: Bytes&amp;lt;32&amp;gt;,
  balance: Uint&amp;lt;128&amp;gt;,
  nonce: Uint&amp;lt;64&amp;gt;
}

// Witness: the actual account data, held privately by user
witness getAccount(): Account;
witness getNullifierSecret(): Bytes&amp;lt;32&amp;gt;;

// Helper: derive a nullifier to prevent double-spending
circuit deriveNullifier(nonce: Uint&amp;lt;64&amp;gt;, secret: Bytes&amp;lt;32&amp;gt;): Field {
  return persistentHash&amp;lt;Vector&amp;lt;2, Field&amp;gt;&amp;gt;(
    [persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(nonce as Bytes&amp;lt;32&amp;gt;), 
     persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(secret)]
  ) as Field;
}

// Helper: commitment to an account
circuit commitToAccount(account: Account, salt: Bytes&amp;lt;32&amp;gt;): Field {
  return persistentHash&amp;lt;Vector&amp;lt;2, Field&amp;gt;&amp;gt;(
    [persistentHash&amp;lt;Account&amp;gt;(account),
     persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(salt)]
  ) as Field;
}

// Main circuit: prove a valid token transfer
export circuit transfer(
  recipient: Bytes&amp;lt;32&amp;gt;,
  amount: Uint&amp;lt;128&amp;gt;,
  salt: Bytes&amp;lt;32&amp;gt;,
  newSalt: Bytes&amp;lt;32&amp;gt;
): [] {
  // Load private account data
  const account = getAccount();
  const nullifierSecret = getNullifierSecret();

  // Verify the account commitment exists
  const oldCommitment = commitToAccount(account, salt);
  assert(
    balanceCommitment[account.owner] == oldCommitment,
    "Account commitment mismatch"
  );

  // Private verification: user has sufficient balance
  assert(account.balance &amp;gt;= amount, "Insufficient balance");

  // Compute new account state (private)
  const newAccount: Account = [
    owner: account.owner,
    balance: account.balance - amount,
    nonce: account.nonce + 1
  ];

  // Create nullifier to prevent replay
  const nullifier = deriveNullifier(account.nonce, nullifierSecret);

  // Update public state with new commitment
  const newCommitment = commitToAccount(newAccount, newSalt);
  balanceCommitment = disclose(account.owner, newCommitment);

  // Record nullifier to prevent double-spend
  // In a real system, this would be a set of spent nullifiers
  // For now, we disclose it as proof of spending
  disclose(nullifier);

  // Recipient balance update (simplified: assume recipient pre-existed)
  // In production, you'd handle account creation
  transferRound.increment(1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Commitment Tradeoff
&lt;/h3&gt;

&lt;p&gt;This approach provides strong privacy but requires careful design:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Balances are never visible on-chain (only commitments)&lt;/li&gt;
&lt;li&gt;Transfers reveal no information except that &lt;em&gt;a&lt;/em&gt; transfer occurred&lt;/li&gt;
&lt;li&gt;The system is composable with other private circuits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Costs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every balance update requires a full commitment recomputation&lt;/li&gt;
&lt;li&gt;Clients must store balance commitments locally (or query from a private oracle)&lt;/li&gt;
&lt;li&gt;Replay protection requires tracking spent nullifiers&lt;/li&gt;
&lt;li&gt;The circuit is more complex, leading to larger proofs and longer proving times&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real world consideration&lt;/strong&gt;: For most applications, you won't implement full commitment schemes from scratch. You'll use Midnight's standard library, which provides optimized versions. But understanding the underlying structure helps you choose the right patterns for your use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  Circuit Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Circuit Optimization Matters
&lt;/h3&gt;

&lt;p&gt;Compact circuits must be bounded at compile time. You can't have unbounded loops or recursive calls. This constraint exists because every circuit must compile to a fixed-size zero-knowledge proof.&lt;/p&gt;

&lt;p&gt;For EVM developers, this is a significant mindset shift. On Ethereum, you pay gas for computation. On Midnight, you accept predetermined computation bounds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This won't compile unbounded recursion
circuit traverse(node: TreeNode): Uint&amp;lt;64&amp;gt; {
  if (node.left == null) {
    return node.value;
  } else {
    return traverse(node.left);
  }
}

// This works—bounded by tree depth
export circuit traverseFixed&amp;lt;#DEPTH&amp;gt;(
  node: TreeNode, 
  path: Vector&amp;lt;#DEPTH, Boolean&amp;gt;
): Uint&amp;lt;64&amp;gt; {
  let current = node;
  for (let i = 0; i &amp;lt; #DEPTH; i++) {
    if (path[i]) {
      current = current.right; // Assumes node structure allows this
    } else {
      current = current.left;
    }
  }
  return current.value;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimization Strategies
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1: Vectorization and Batching
&lt;/h4&gt;

&lt;p&gt;For operations on multiple items, vectorize instead of looping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Less efficient: separate proofs for each item
export circuit verifyAge(secret: Bytes&amp;lt;32&amp;gt;, minAge: Uint&amp;lt;32&amp;gt;): [] {
  const birthDate = getUserBirthDate();
  assert(currentTime - birthDate &amp;gt;= minAge, "Too young");
}

// More efficient: batch verification
export circuit verifyAgesInBatch&amp;lt;#N&amp;gt;(
  secrets: Vector&amp;lt;#N, Bytes&amp;lt;32&amp;gt;&amp;gt;,
  minAges: Vector&amp;lt;#N, Uint&amp;lt;32&amp;gt;&amp;gt;
): [] {
  for (let i = 0; i &amp;lt; #N; i++) {
    // Witness supplies ages for all users
    const birthDates = getUserBirthDates(i);
    assert(
      currentTime - birthDates &amp;gt;= minAges[i],
      "Age check failed"
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2: Lazy Evaluation with Merkle Trees
&lt;/h4&gt;

&lt;p&gt;Instead of processing all data inline, use Merkle trees to prove membership:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Direct approach: verify all items (O(n) circuit size)
export circuit verifyAllBalances&amp;lt;#N&amp;gt;(
  balances: Vector&amp;lt;#N, Uint&amp;lt;128&amp;gt;&amp;gt;,
  totalRequired: Uint&amp;lt;128&amp;gt;
): [] {
  let sum: Uint&amp;lt;128&amp;gt; = 0;
  for (let i = 0; i &amp;lt; #N; i++) {
    sum = sum + balances[i];
  }
  assert(sum &amp;gt;= totalRequired, "Insufficient total");
}

// Optimized: verify membership in Merkle tree (O(log n) circuit size)
export circuit verifyBalanceProof(
  balance: Uint&amp;lt;128&amp;gt;,
  merkleProof: Vector&amp;lt;32, Field&amp;gt;, // Log2(2^32) = 32 levels
  merkleRoot: Field,
  leaf_index: Uint&amp;lt;32&amp;gt;
): [] {
  // Recompute leaf and verify path
  const leaf = persistentHash&amp;lt;Uint&amp;lt;128&amp;gt;&amp;gt;(balance) as Field;
  let current = leaf;

  for (let i = 0; i &amp;lt; 32; i++) {
    const proofElement = merkleProof[i];
    // Combine in canonical order to prevent tree structure attacks
    if (leaf_index &amp;amp; (1 &amp;lt;&amp;lt; i) == 0) {
      current = persistentHash&amp;lt;Vector&amp;lt;2, Field&amp;gt;&amp;gt;([current, proofElement]) as Field;
    } else {
      current = persistentHash&amp;lt;Vector&amp;lt;2, Field&amp;gt;&amp;gt;([proofElement, current]) as Field;
    }
  }

  assert(current == merkleRoot, "Merkle proof failed");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3: Proof Aggregation
&lt;/h4&gt;

&lt;p&gt;When you have multiple privacy preserving properties to prove, you have two choices: prove them all in one circuit (larger proof), or split into separate circuits (multiple proofs, sequential verification).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Single circuit: proves age AND asset ownership
// Proof size: large, proving time: high
export circuit verifyAgeAndAssets(
  secret: Bytes&amp;lt;32&amp;gt;,
  minAge: Uint&amp;lt;32&amp;gt;,
  assetsProof: AssetsProof
): [] {
  const birthDate = getUserBirthDate();
  assert(currentTime - birthDate &amp;gt;= minAge, "Too young");

  const assets = getAssets(assetsProof);
  assert(assets.value &amp;gt;= 100000, "Insufficient assets");
}

// Split circuits: separate concerns, compose on app level
// Proof size: smaller per circuit, proving time: faster
export circuit verifyAge(secret: Bytes&amp;lt;32&amp;gt;, minAge: Uint&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
  const birthDate = getUserBirthDate();
  assert(currentTime - birthDate &amp;gt;= minAge, "Too young");
  return disclose(persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(secret));
}

export circuit verifyAssets(assetsProof: AssetsProof): Bytes&amp;lt;32&amp;gt; {
  const assets = getAssets(assetsProof);
  assert(assets.value &amp;gt;= 100000, "Insufficient assets");
  return disclose(persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(assetsProof));
}

// Application composes both proofs
// Tradeoff: two proofs to verify, but faster to prove each one
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmarking Your Circuits
&lt;/h3&gt;

&lt;p&gt;Different circuit structures have dramatic performance differences. Use this framework to evaluate:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Structure&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;th&gt;Use When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct computation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple, straightforward&lt;/td&gt;
&lt;td&gt;Large proof, slow&lt;/td&gt;
&lt;td&gt;Small bounded operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merkle proof verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logarithmic size, scales well&lt;/td&gt;
&lt;td&gt;Higher cryptographic complexity&lt;/td&gt;
&lt;td&gt;Membership checks in large sets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vectorized batching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Efficient for repeated ops&lt;/td&gt;
&lt;td&gt;Requires uniform structure&lt;/td&gt;
&lt;td&gt;Batch processing many similar items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Split circuits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Faster per-circuit proving&lt;/td&gt;
&lt;td&gt;Coordination overhead&lt;/td&gt;
&lt;td&gt;When proofs are logically independent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Multi-Contract Privacy Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Composing Private Contracts
&lt;/h3&gt;

&lt;p&gt;Midnight's biggest strength is that multiple private contracts can interact while maintaining privacy boundaries. However, composing contracts introduces new considerations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem: The Metadata Leak
&lt;/h3&gt;

&lt;p&gt;Even if all data is encrypted, metadata can leak information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Privacy leak: contract call pattern reveals intent
export circuit buyAsset(assetId: Uint&amp;lt;64&amp;gt;): Bytes&amp;lt;32&amp;gt; {
  // If specific assetIds always correlate with specific users,
  // blockchain analysis can link buyers to assets even without seeing amounts
  const proof = getOwnershipProof(assetId);
  verify(proof);
  return disclose(persistentHash&amp;lt;Uint&amp;lt;64&amp;gt;&amp;gt;(assetId));
}

// Mitigated: hide specific asset, batch with dummy calls
export circuit batchBuyAssets&amp;lt;#N&amp;gt;(
  assetIds: Vector&amp;lt;#N, Uint&amp;lt;64&amp;gt;&amp;gt;,
  proofs: Vector&amp;lt;#N, Bytes&amp;lt;32&amp;gt;&amp;gt;,
  isReal: Vector&amp;lt;#N, Boolean&amp;gt;
): Vector&amp;lt;#N, Bytes&amp;lt;32&amp;gt;&amp;gt; {
  let results: Vector&amp;lt;#N, Bytes&amp;lt;32&amp;gt;&amp;gt; = [];
  for (let i = 0; i &amp;lt; #N; i++) {
    // Verify proof only if real purchase (circuits execute either way)
    if (isReal[i]) {
      verify(proofs[i]);
    }
    results[i] = disclose(persistentHash&amp;lt;Uint&amp;lt;64&amp;gt;&amp;gt;(assetIds[i]));
  }
  return results;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pattern: Shielded Contract Composition
&lt;/h3&gt;

&lt;p&gt;When one private contract depends on another, you need a protocol for safe interaction:&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;

// Contract A: Identity registry
export ledger identityCommitment: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Field&amp;gt;;

export circuit registerIdentity(publicKey: Bytes&amp;lt;32&amp;gt;): [] {
  const commitment = persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(publicKey) as Field;
  identityCommitment = disclose(publicKey, commitment);
}

// Contract B: Private voting (depends on Contract A)
export ledger voteCommitment: Map&amp;lt;Field, Field&amp;gt;; // (identityHash, voteHash)

export circuit castVote(
  publicKey: Bytes&amp;lt;32&amp;gt;,
  voteChoice: Uint&amp;lt;8&amp;gt;,
  salt: Bytes&amp;lt;32&amp;gt;
): [] {
  // Prove participation in Contract A without revealing identity
  const commitment = persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(publicKey) as Field;
  assert(identityCommitment[publicKey] == commitment, "Not registered");

  // Cast vote privately
  const voteHash = persistentHash&amp;lt;Vector&amp;lt;2, Field&amp;gt;&amp;gt;(
    [persistentHash&amp;lt;Uint&amp;lt;8&amp;gt;&amp;gt;(voteChoice) as Field, 
     persistentHash&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;(salt) as Field]
  ) as Field;

  voteCommitment = disclose(commitment, voteHash);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key insight&lt;/strong&gt;: Contract B proves it respects Contract A's invariants (the user is registered) without revealing the user's identity. This is the foundation of composable privacy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Considerations: State Consistency
&lt;/h3&gt;

&lt;p&gt;When multiple contracts touch shared state, you must be careful about ordering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Race condition possible: State updated after verification
export circuit transferWithFeeShare(
  amount: Uint&amp;lt;128&amp;gt;,
  feeRecipient: Bytes&amp;lt;32&amp;gt;
): [] {
  const balance = getBalance(); // Witness
  assert(balance &amp;gt;= amount + fee, "Insufficient");

  // Race condition: if another proof updates fee rate before on-chain execution,
  // this assertion might have been based on stale assumptions
  const currentFee = feeContract.queryFee();
}

//  Fixed: Include fee data in the proof
export circuit transferWithFeeShare(
  amount: Uint&amp;lt;128&amp;gt;,
  feeRecipient: Bytes&amp;lt;32&amp;gt;,
  expectedFeeRate: Uint&amp;lt;16&amp;gt;, // App provides expected fee
  feeProof: Bytes&amp;lt;32&amp;gt; // Proof that fee rate matches
): [] {
  const balance = getBalance();

  // Verify fee was what we expected at proof time
  verify(feeProof);

  const fee = (amount * expectedFeeRate) / 100000;
  assert(balance &amp;gt;= amount + fee, "Insufficient");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real-World Tradeoffs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Decision Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Witness Source&lt;/th&gt;
&lt;th&gt;Proof Strategy&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Privacy-first tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commitments + Nullifiers&lt;/td&gt;
&lt;td&gt;User secret key&lt;/td&gt;
&lt;td&gt;Split by operation type&lt;/td&gt;
&lt;td&gt;Requires nullifier tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KYC/AML compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Age/identity verification&lt;/td&gt;
&lt;td&gt;Credential issuer&lt;/td&gt;
&lt;td&gt;Selective disclosure&lt;/td&gt;
&lt;td&gt;Issuer must be trusted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DAO voting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shielded voting + identity registry&lt;/td&gt;
&lt;td&gt;User secret, registry contract&lt;/td&gt;
&lt;td&gt;Batched dummy votes&lt;/td&gt;
&lt;td&gt;Metadata still visible (voting time)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Asset swaps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DEX with private pricing**&lt;/td&gt;
&lt;td&gt;Oracle feeds, user orders&lt;/td&gt;
&lt;td&gt;Batch matching&lt;/td&gt;
&lt;td&gt;Requires MEV-resistant ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Debugging &amp;amp; Testing Advanced Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Console Logging in Compact
&lt;/h3&gt;

&lt;p&gt;While developing, use logging carefully (it's not available in production proofs):&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 debugTransfer(
  recipient: Bytes&amp;lt;32&amp;gt;,
  amount: Uint&amp;lt;128&amp;gt;
): [] {
  const balance = getBalance();

  // Debug logging helps during development
  assert(balance &amp;gt;= amount, "Insufficient");

  // The proof doesn't include debug output, but your app runner sees it
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing Strategy for Circuits
&lt;/h3&gt;

&lt;p&gt;Since circuits must be proven, test thoroughly before deployment:&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="c1"&gt;// TypeScript test harness&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Contract&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@midnight-protocol/sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compiledCircuit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Test 1: Valid transfer&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validTransfer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;randomSalt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;newSalt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newRandomSalt&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validTransfer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Valid transfer should produce proof&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Test 2: Insufficient balance should fail&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invalidTransfer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;999999999999&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;randomSalt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;newSalt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newRandomSalt&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invalidTransfer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proof&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid transfer should not produce proof&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Advanced Compact patterns aren't just optimizations they're architectural decisions that shape what's possible in your application.&lt;/p&gt;

&lt;p&gt;As you move from learning Compact to building production systems, keep these principles in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Witnesses are your privacy boundary.&lt;/strong&gt; Choose witness sources carefully; they determine your security model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commitments enable privacy at scale.&lt;/strong&gt; Direct state disclosure doesn't mix with zero-knowledge proofs; use commitments instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Circuits must be bounded, but cleverly.&lt;/strong&gt; Vectorization, Merkle trees, and proof aggregation let you handle complexity without exceeding bounds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Composition requires explicit coordination.&lt;/strong&gt; Multiple private circuits can interact, but metadata and state consistency need careful handling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy and usability are in tension.&lt;/strong&gt; Batching dummy transactions protects metadata but increases proof sizes. Splitting circuits proves faster but requires coordination. Choose based on your threat model.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Midnight gives you powerful tools for building privacy-first applications. Mastering these patterns lets you use them effectively.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Midnight Language Reference&lt;/strong&gt;: &lt;a href="https://docs.midnight.network/develop/reference/compact" rel="noopener noreferrer"&gt;Full Compact syntax and semantics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Disclosure Deep Dive&lt;/strong&gt;: Understanding the &lt;code&gt;disclose()&lt;/code&gt; wrapper and threat models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Knowledge Proof Fundamentals&lt;/strong&gt;: If you want to understand the cryptography behind Compact circuits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Previous in this series&lt;/strong&gt;: &lt;a href="https://dev.tolink-to-samantha-post"&gt;Learning Web3 from the Ground Up&lt;/a&gt; by Samantha Holstine&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>blockchain</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Midnight Mainnet Is Live. The Privacy Stack Just Got Real.</title>
      <dc:creator>Barnabas</dc:creator>
      <pubDate>Thu, 02 Apr 2026 16:37:39 +0000</pubDate>
      <link>https://dev.to/midnight-aliit/midnight-mainnet-is-live-the-privacy-stack-just-got-real-4d65</link>
      <guid>https://dev.to/midnight-aliit/midnight-mainnet-is-live-the-privacy-stack-just-got-real-4d65</guid>
      <description>&lt;p&gt;If you've been following the Midnight Network since its early testnet days, you already know the core pitch: a fourth-generation blockchain designed around programmable privacy, zero-knowledge proofs, and selective disclosure. After years of groundwork, multiple hackathon cycles, a massive token distribution, and a developer ecosystem that quietly grew into something serious, mainnet is live.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;March 30, 2026&lt;/strong&gt;, the Midnight genesis block was produced. Developers, institutions, and partners can now deploy applications and migrate assets on a live production chain. This isn't vaporware anymore. This is a running network.&lt;/p&gt;

&lt;p&gt;Let me break down what actually happened over the past month, what's technically significant about the launch architecture, and where this ecosystem is heading.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Launch: What "Federated Mainnet" Actually Means
&lt;/h2&gt;

&lt;p&gt;One thing worth clarifying upfront: the Midnight mainnet is launching in a &lt;strong&gt;federated model&lt;/strong&gt;, not a fully decentralized one. Hoskinson himself called it a "guarded era." That's intentional, and it's actually a smart engineering decision.&lt;/p&gt;

&lt;p&gt;The initial validator set consists of &lt;strong&gt;nine federated node partners&lt;/strong&gt;: Worldpay, Bullish, MoneyGram, Pairpoint by Vodafone, eToro, AlphaTON Capital, Google Cloud, Blockdaemon, and Shielded Technologies. These are institutional-grade operators running infrastructure under explicit participation rules. The goal is to launch a stable, secure network where the early DApps being deployed have a hardened foundation to run on, not to rush decentralization before the system is ready for it.&lt;/p&gt;

&lt;p&gt;There are already &lt;strong&gt;130+ post-launch bug fixes queued&lt;/strong&gt;, none critical, but the team expects to spend two to three weeks hardening the system. This is exactly the right approach for a network whose primary value proposition is &lt;em&gt;security and privacy&lt;/em&gt;. You can't afford to rush that.&lt;/p&gt;

&lt;p&gt;The move to broader decentralization, bringing Cardano Stake Pool Operators online, launching the DUST Capacity Exchange, and enabling community-driven block production, comes in the next roadmap phase, &lt;strong&gt;Mōhalu&lt;/strong&gt;, targeted for mid-2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture: What Makes Midnight Different Under the Hood
&lt;/h2&gt;

&lt;p&gt;There's a lot of noise in the privacy blockchain space, and most of it is about anonymity. Midnight is doing something different: &lt;em&gt;programmable&lt;/em&gt; privacy, which means developers control the privacy rules, not the protocol forcing one-size-fits-all opacity.&lt;/p&gt;

&lt;p&gt;Here's what the architecture actually looks like:&lt;/p&gt;

&lt;h3&gt;
  
  
  Hybrid Dual-State Model
&lt;/h3&gt;

&lt;p&gt;Midnight implements a &lt;strong&gt;combined UTXO and account-based model&lt;/strong&gt; in a single atomic step. Public state lives on the ledger (unshielded), and private state lives in an off-chain execution environment. The &lt;strong&gt;Kachina Protocol&lt;/strong&gt; is the bridge between these two worlds. It lets you process private state transitions off-chain and then submit only a zero-knowledge proof to the public ledger. The network verifies that your computation was valid without ever seeing the underlying data.&lt;/p&gt;

&lt;p&gt;This is significantly different from, say, Zcash's approach (which shielded &lt;em&gt;transactions&lt;/em&gt; rather than &lt;em&gt;computation&lt;/em&gt;) or Tornado Cash-style mixing (which is really just obfuscating transfers). What Midnight enables is ZK-verified &lt;em&gt;application logic&lt;/em&gt;, including compliance checks, eligibility proofs, and identity verification, all done without the sensitive data touching the chain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Proofs
&lt;/h3&gt;

&lt;p&gt;Here's one of the design decisions that matters most from a privacy standpoint: &lt;strong&gt;ZK proofs are generated client-side&lt;/strong&gt;. Using a local proof server, sensitive data stays on the user's device. The proof is what gets submitted to the network, not the underlying information. So when you're proving you meet some eligibility threshold, or that you hold a certain credential, that data never leaves your machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shielded and Unshielded Assets
&lt;/h3&gt;

&lt;p&gt;The network supports both. Shielded assets keep balances, counterparties, and transaction flows off the public ledger. Unshielded assets provide full on-chain visibility, useful for things like DeFi where you need composability and public auditability. Developers can mix both in a single application, choosing at the contract level what must stay private and what can be public.&lt;/p&gt;

&lt;p&gt;This is what "selective disclosure" means in practice: you can build a DeFi protocol that verifies a user's KYC status without storing their personal data on-chain. The compliance proof goes on-chain; the identity stays off-chain.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dual-Token Model: NIGHT and DUST
&lt;/h2&gt;

&lt;p&gt;This is one of the more technically interesting tokenomic designs in recent blockchain history, and it's worth understanding properly before dismissing it as complexity for complexity's sake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NIGHT&lt;/strong&gt; is the utility and governance token. It launched on Cardano as a native asset back in December 2025 (the Hilo phase), and it's now mirrored onto the Midnight ledger via a protocol-level mechanism that prevents value duplication across both chains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DUST&lt;/strong&gt; is the network resource token, used to pay for transactions and smart contract execution. The key property of DUST is that it &lt;em&gt;regenerates over time&lt;/em&gt; based on NIGHT holdings. A full recharge takes seven days. You don't spend NIGHT on transactions; you spend DUST, which regenerates from your NIGHT holdings.&lt;/p&gt;

&lt;p&gt;Why does this matter? It separates capital assets from operational costs. In most Layer 1s, every transaction competes for the same token that's also your store of value and governance instrument. That creates volatile transaction costs tied to market speculation. Midnight's model makes transaction costs significantly more predictable, especially important for enterprise applications that need to budget for on-chain compute in advance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Compact: The ZK Smart Contract Language
&lt;/h2&gt;

&lt;p&gt;This is the piece of the puzzle that determines whether developers actually build on this network or just admire it from a distance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compact&lt;/strong&gt; is a statically-typed domain-specific language built on TypeScript syntax, designed specifically for writing privacy-preserving smart contracts on Midnight. The core design goal is to eliminate the need for deep cryptographic expertise to build ZK applications.&lt;/p&gt;

&lt;p&gt;Most ZK development today requires intimate knowledge of circuit design, proof system internals, and constraint systems. Compact abstracts that away. You write familiar TypeScript-style code, manage both public and private state within a single contract, and the compiler handles the ZK proof generation machinery underneath.&lt;/p&gt;

&lt;p&gt;The current stable version as of mainnet launch is &lt;strong&gt;Compact 0.28.0&lt;/strong&gt;, paired with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;midnight-js 3.0.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wallet-sdk 1.0.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Proof Server 7.0.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The team recently overhauled the Ledger to v7.0.0 as part of mainnet prep, which included switching to Midnight SRS and &lt;code&gt;midnight-zk 1.0&lt;/code&gt;, implementing dimension-based pricing for transactions, and integrating fixes from external security audits.&lt;/p&gt;

&lt;p&gt;For developers migrating existing projects to preprod (which everyone should be doing now), the core scaffolding tool &lt;code&gt;create-mn-app&lt;/code&gt; makes bootstrapping a new Midnight project straightforward via npm. The reference DApps, Counter and Bulletin Board, are fully updated for the preprod environment and serve as solid starting points for understanding the contract model.&lt;/p&gt;

&lt;p&gt;One underrated tool that shipped earlier this year: the &lt;strong&gt;MCP server for Midnight&lt;/strong&gt;. General-purpose AI coding assistants like Claude, Cursor, and Copilot don't have training data on Compact, so they generate hallucinated or invalid code. The MCP server bridges that gap by giving AI coding tools direct, structured access to the Compact codebase and static analysis tools. It's been downloaded over 6,000 times via NPM, a good signal that the developer experience tooling is being taken seriously.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ecosystem: What's Actually Being Built
&lt;/h2&gt;

&lt;p&gt;The Aliit Fellowship launched late last year as Midnight's technical leadership program, with 17 fellows from 11 countries, including ZK researchers, open-source maintainers, and educators. This cohort is actively building reference architectures, localizing technical documentation, and mentoring new developers coming through the Midnight Academy.&lt;/p&gt;

&lt;p&gt;The Academy itself recently released a new hands-on module focused on actually &lt;em&gt;building&lt;/em&gt; DApps rather than just understanding ZK theory. If you've completed the foundational courses on zero-knowledge proofs, the new module walks you through writing Compact smart contracts and wiring them into a functional application end-to-end.&lt;/p&gt;

&lt;p&gt;Network metrics tell the story of accelerating builder activity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1,617% surge in smart contract deployments&lt;/strong&gt; recorded in November during the Midnight Summit hackathon period&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;35% rise in smart contract deployments&lt;/strong&gt; month-over-month in December&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;19% increase in block producers&lt;/strong&gt; in the same period&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;120+ builders&lt;/strong&gt; engaged during the Summit hackathon alone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't just engagement numbers. Smart contract deployments on a testnet are a leading indicator of what's going to show up on mainnet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Interoperability: LayerZero and USDCx
&lt;/h2&gt;

&lt;p&gt;Two announcements from Consensus Hong Kong in February are worth highlighting because they significantly expand Midnight's surface area.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LayerZero integration&lt;/strong&gt; is probably the biggest interoperability unlock in the ecosystem's history. LayerZero connects over 160 blockchains. For Midnight, this means zero-knowledge proofs and selective disclosure can extend &lt;em&gt;across&lt;/em&gt; blockchain networks. You're not locked into the Midnight ecosystem to benefit from its privacy architecture. This is the "Hua" phase vision, cross-chain Hybrid DApps, starting to take shape now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;USDCx&lt;/strong&gt;, a regulatory-compliant stablecoin mirrored 1:1 with Circle's USDC via xReserve infrastructure, launched on Cardano ahead of mainnet. The historical liquidity gap in the Cardano/Midnight ecosystem has been a real limitation for DeFi use cases. USDCx addresses that directly, providing institutional-grade collateral that can flow into Midnight-native protocols.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming Next: The Roadmap Ahead
&lt;/h2&gt;

&lt;p&gt;The four-phase roadmap gives a clear picture of trajectory:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kūkolu (Now)&lt;/strong&gt; — Federated mainnet is live. First wave of production DApps deploying. Developer tooling stabilizing. This is where we are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mōhalu (Mid-2026)&lt;/strong&gt; — Broader decentralization kicks in. Cardano Stake Pool Operators come online as block producers. The DUST Capacity Exchange activates, letting NIGHT holders trade their DUST generation capacity. Staking rewards go live, creating economic incentives for network participation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hua (Late 2026)&lt;/strong&gt; — The Hybrid DApp era. Midnight's privacy layer becomes embeddable into applications on other chains. This is when it stops being a standalone blockchain and starts functioning as privacy infrastructure for the broader Web3 ecosystem.&lt;/p&gt;

&lt;p&gt;Each phase follows the previous one rather than running in parallel, which is a sensible sequencing decision. You can't properly decentralize a network until the production environment is stable. You can't build Hybrid DApps into other ecosystems until your interoperability rails (LayerZero, USDCx) are proven in production. The ordering matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Take
&lt;/h2&gt;

&lt;p&gt;What struck me most over the past month is how the technical decisions compound on each other. Client-side proof generation protects data. Compact removes the expertise barrier to ZK development. The federated validator set provides stability without premature decentralization. The DUST model makes transaction costs predictable for enterprise use cases. None of these are flashy standalone features; they're design choices that collectively make real-world deployment practical rather than theoretical.&lt;/p&gt;

&lt;p&gt;The next few months will tell us whether the builder ecosystem actually produces compelling DApps, and whether the federated network stays stable as more applications come online. There are 130+ fixes being worked through right now, which is actually a healthy sign. It means the testnet cycle did its job of surfacing real issues before they hit production.&lt;/p&gt;

&lt;p&gt;For anyone building in the privacy or compliance space, Midnight's mainnet going live is worth paying close attention to. The infrastructure is here. The question now is what gets built on it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Resources:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://midnight.network" rel="noopener noreferrer"&gt;Midnight Developer Hub&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://docs.midnight.network" rel="noopener noreferrer"&gt;Compact Documentation&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://midnight.network/academy" rel="noopener noreferrer"&gt;Midnight Developer Academy&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://midnight.network/blog" rel="noopener noreferrer"&gt;Aliit Fellowship&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>developer</category>
      <category>community</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
