<?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>Forem: BossChaos</title>
    <description>The latest articles on Forem by BossChaos (@bosschaos).</description>
    <link>https://forem.com/bosschaos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3908415%2F7e9679b7-a452-4c5d-bfe1-61ed802df719.png</url>
      <title>Forem: BossChaos</title>
      <link>https://forem.com/bosschaos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bosschaos"/>
    <language>en</language>
    <item>
      <title>Building Privacy-First Apps with Midnight Easy SDK</title>
      <dc:creator>BossChaos</dc:creator>
      <pubDate>Sun, 03 May 2026 11:48:38 +0000</pubDate>
      <link>https://forem.com/bosschaos/building-privacy-first-apps-with-midnight-easy-sdk-2aca</link>
      <guid>https://forem.com/bosschaos/building-privacy-first-apps-with-midnight-easy-sdk-2aca</guid>
      <description>&lt;h1&gt;
  
  
  🌙 Midnight Easy SDK: Privacy-First Development Made Simple
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is a submission for the &lt;a href="https://dev.to/challenges/midnight-2025-08-20"&gt;Midnight Network "Privacy First" Challenge&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Track:&lt;/strong&gt; Enhance the Ecosystem + Best Tutorial&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License:&lt;/strong&gt; Apache 2.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/BossChaos/midnight-easy-sdk" rel="noopener noreferrer"&gt;github.com/BossChaos/midnight-easy-sdk&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  📌 Project Overview
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/midnight"&gt;@midnight&lt;/a&gt;/easy-sdk&lt;/strong&gt; to lower the barrier for developers who want to add privacy features to their apps but don't want to become zero-knowledge cryptography experts.&lt;/p&gt;

&lt;p&gt;The SDK wraps Midnight's cryptographic primitives in a clean TypeScript API. You get &lt;strong&gt;seal&lt;/strong&gt;, &lt;strong&gt;verify&lt;/strong&gt;, and &lt;strong&gt;decrypt&lt;/strong&gt; operations, plus &lt;strong&gt;React hooks&lt;/strong&gt; that handle loading states and error recovery out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI Framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React + Hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cryptography&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Midnight ZK circuits (Noir)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vitest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  💡 Core Value
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No ZK expertise required&lt;/strong&gt; — Seal data, verify proofs, and decrypt results with three function calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React-first design&lt;/strong&gt; — &lt;code&gt;useSeal&lt;/code&gt;, &lt;code&gt;useVerify&lt;/code&gt;, &lt;code&gt;useDecrypt&lt;/code&gt; hooks handle all the async complexity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective disclosure&lt;/strong&gt; — Reveal only the metadata you choose, keeping the rest private&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop-in installation&lt;/strong&gt; — &lt;code&gt;npm install @midnight/easy-sdk&lt;/code&gt; and you're ready to go&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Quick Start
&lt;/h2&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/easy-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;MidnightEasy&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/easy-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;midnight&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;MidnightEasy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testnet&lt;/span&gt;&lt;span class="dl"&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Core API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Seal Data
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;seal()&lt;/code&gt; creates a confidential commitment from your data. Under the hood, it generates a zero-knowledge note that hides the input while allowing later selective disclosure.&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;sealed&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;value&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;_000_000n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;did:mad: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;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escrow&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;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="nx"&gt;sealed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// '0xabc123...'&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="nx"&gt;sealed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nullifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// '0xdef456...'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;commitment&lt;/code&gt; is posted to the Midnight ledger. Observers see a hash — not the underlying value.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Verify a Proof
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;verify()&lt;/code&gt; checks a zero-knowledge proof against a commitment without revealing the sealed data.&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;result&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sealed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="nx"&gt;_000n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;allowedAssets&lt;/span&gt;&lt;span class="p"&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;USDC&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;proof&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userProvidedProof&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// true&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="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// { purpose: 'escrow' }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Decrypt Results
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;decrypt()&lt;/code&gt; reveals the plaintext to authorized recipients only, using Midnight's threshold decryption.&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;plaintext&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encryptedResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;recipientKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&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;h2&gt;
  
  
  React Hooks
&lt;/h2&gt;

&lt;p&gt;For UI-driven privacy flows, the SDK provides React hooks:&lt;br&gt;
&lt;/p&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;MidnightProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSeal&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/easy-sdk/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="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;MidnightProvider&lt;/span&gt; &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"testnet"&lt;/span&gt; &lt;span class="na"&gt;autoConnect&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EscrowPanel&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;MidnightProvider&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;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;EscrowPanel&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;seal&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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSeal&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;handleSeal&lt;/span&gt; &lt;span class="o"&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;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;seal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="nx"&gt;_000n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myDID&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="s1"&gt;Committed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commitment&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSeal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Seal&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example: Private Escrow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Alice seals her deposit&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deposit&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="nx"&gt;_000n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aliceDID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;depositor&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="c1"&gt;// Bob seals his acceptance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acceptance&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;value&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="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USDC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bobDID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;acceptor&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="c1"&gt;// Contract verifies both&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;valid&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;midnight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;allowedAssets&lt;/span&gt;&lt;span class="p"&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;USDC&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Open source under Apache 2.0. Contributions welcome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📖 Improve documentation&lt;/li&gt;
&lt;li&gt;🧪 Expand test coverage&lt;/li&gt;
&lt;li&gt;🛠️ Add new primitives and hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/BossChaos/midnight-easy-sdk" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for the Midnight Network "Privacy First" Challenge — Enhance the Ecosystem &amp;amp; Best Tutorial tracks.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Governance Token dApp on Midnight</title>
      <dc:creator>BossChaos</dc:creator>
      <pubDate>Sun, 03 May 2026 08:15:36 +0000</pubDate>
      <link>https://forem.com/bosschaos/building-a-governance-token-dapp-on-midnight-1llp</link>
      <guid>https://forem.com/bosschaos/building-a-governance-token-dapp-on-midnight-1llp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Complete Source Code:&lt;/strong&gt; &lt;a href="https://github.com/BossChaos/midnight-governance-token" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Network:&lt;/strong&gt; Midnight Preprod&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Compact + TypeScript + React + Vite&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Midnight is famous for privacy-first smart contracts using zero-knowledge proofs. But &lt;strong&gt;not everything needs to be private&lt;/strong&gt;. Sometimes you want transparent, publicly verifiable transactions — like community governance tokens, loyalty points, or public reward systems.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will build a &lt;strong&gt;complete unshielded token dApp&lt;/strong&gt; on Midnight with role-based minting, transfer memos, and balance snapshots for governance voting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Unshielded Tokens
&lt;/h2&gt;

&lt;p&gt;Unshielded tokens are perfect for governance because transparency builds trust. Unlike shielded tokens that hide balances and transfers behind ZK proofs, unshielded tokens offer lower gas costs and faster confirmation times.&lt;/p&gt;

&lt;p&gt;Use cases include governance voting, public reward systems, and loyalty programs where auditability matters more than privacy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Smart Contract
&lt;/h2&gt;

&lt;p&gt;Our GovernanceToken contract implements three key features for decentralized governance:&lt;/p&gt;

&lt;h3&gt;
  
  
  Role-Based Minting
&lt;/h3&gt;

&lt;p&gt;Instead of a single minter, the contract uses a minter role system. The deployer can grant or revoke minting permissions to other addresses, enabling multi-sig governance or DAO-controlled token issuance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transfer with Memo
&lt;/h3&gt;

&lt;p&gt;Every transfer supports an optional 64-byte memo field. This creates an on-chain audit trail useful for payment references, vote tracking, and compliance reporting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Balance Snapshots
&lt;/h3&gt;

&lt;p&gt;For governance voting, you need to know a user balance at a specific point in time. Snapshots lock in balances so users cannot game the system by transferring tokens after a vote is announced.&lt;/p&gt;




&lt;h2&gt;
  
  
  TypeScript Integration
&lt;/h2&gt;

&lt;p&gt;The TypeScript layer connects the React frontend to the Midnight blockchain through the DApp Connector API. It handles wallet building, contract deployment, and transaction submission.&lt;/p&gt;

&lt;p&gt;Key components include the WalletBuilder for seed-based wallet creation, the IndexerProvider for balance queries, and the NodeZkConfigProvider for proof generation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the UI
&lt;/h2&gt;

&lt;p&gt;The React frontend provides four main panels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;WalletConnect&lt;/strong&gt; handles wallet discovery and connection state management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BalanceDisplay&lt;/strong&gt; shows token balances with real-time refresh from the indexer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MintPanel&lt;/strong&gt; provides a form for authorized minters to issue new tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TransferPanel&lt;/strong&gt; enables token transfers with memo field support&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The UI uses TailwindCSS for a dark theme optimized for developer workflows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing and Deployment
&lt;/h2&gt;

&lt;p&gt;Before going live, test all contract functions against the Midnight local Docker stack. The deployment script handles wallet initialization, contract compilation, and transaction submission.&lt;/p&gt;

&lt;p&gt;Run the integration test suite to verify minting permissions, transfer memo encoding, and snapshot accuracy before deploying to preprod.&lt;/p&gt;




&lt;p&gt;Full source code available at &lt;a href="https://github.com/BossChaos/midnight-governance-token" rel="noopener noreferrer"&gt;https://github.com/BossChaos/midnight-governance-token&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web3</category>
    </item>
    <item>
      <title>Anonymous Membership Proofs on Midnight: Building Privacy-Preserving Allowlists</title>
      <dc:creator>BossChaos</dc:creator>
      <pubDate>Sat, 02 May 2026 13:05:55 +0000</pubDate>
      <link>https://forem.com/bosschaos/anonymous-membership-proofs-on-midnight-building-privacy-preserving-allowlists-mge</link>
      <guid>https://forem.com/bosschaos/anonymous-membership-proofs-on-midnight-building-privacy-preserving-allowlists-mge</guid>
      <description>&lt;h1&gt;
  
  
  Anonymous Membership Proofs on Midnight: Building Privacy-Preserving Allowlists
&lt;/h1&gt;

&lt;p&gt;Last month, I was tasked with building an allowlist system for a Midnight dApp. The requirement seemed simple: let authorized users access a feature without revealing who they are. In the clear-text world, you'd just check &lt;code&gt;if (user in allowedList)&lt;/code&gt;. But on a privacy platform, that &lt;code&gt;if&lt;/code&gt; statement leaks everything.&lt;/p&gt;

&lt;p&gt;This tutorial walks through building a complete anonymous membership proof system — from the Compact contract on-chain to the TypeScript tooling that generates Merkle proofs locally. We'll cover sparse Merkle trees, depth-20 path verification, nullifier-based replay prevention, and admin root management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Merkle Trees for Allowlists?
&lt;/h2&gt;

&lt;p&gt;Traditional allowlists publish every member's address on-chain. That's fine for transparency, but terrible for privacy. A Merkle tree solves this differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Off-chain&lt;/strong&gt;: The admin maintains a list of member secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-chain&lt;/strong&gt;: Only a single 32-byte hash (the Merkle root) is stored&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof&lt;/strong&gt;: A member proves they know a secret that hashes to a leaf in the tree, without revealing which leaf
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Root (on-chain)
                   /    \
                 H01    H23
                /  \    /  \
               H0  H1  H2  H3
              / \  / \ / \ / \
             L0 L1 L2 L3 ...  (2^20 leaves)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prove you're L1, you provide H0, H23, and the path indices. The verifier recomputes the root and checks it matches the on-chain value. Your secret (L1's preimage) stays private.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compact Contract
&lt;/h2&gt;

&lt;p&gt;The contract manages three pieces of state:&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 merkle_root: Bytes&amp;lt;32&amp;gt;;
export ledger admin_commitment: Bytes&amp;lt;32&amp;gt;;
export ledger used_nullifiers: Set&amp;lt;Bytes&amp;lt;32&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Witnesses (Secret Inputs)
&lt;/h3&gt;

&lt;p&gt;These are the prover-side inputs that never appear on-chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;witness getSecret(): Bytes&amp;lt;32&amp;gt;;
witness getContext(): Bytes&amp;lt;32&amp;gt;;
witness getSiblings(): Vector&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;;
witness getPathIndices(): Vector&amp;lt;20, Boolean&amp;gt;;
witness getAdminSecret(): Bytes&amp;lt;32&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recomputing the Merkle Path
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit hashLevelNode(is_right: Boolean, current: Bytes&amp;lt;32&amp;gt;, sibling: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt; {
  if (is_right) {
    return persistentHash&amp;lt;Vector&amp;lt;3, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
      pad(32, "zk-allowlist:node:v1"),
      sibling,
      current
    ]);
  } else {
    return persistentHash&amp;lt;Vector&amp;lt;3, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
      pad(32, "zk-allowlist:node:v1"),
      current,
      sibling
    ]);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Membership
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit isMember(): (Bytes&amp;lt;32&amp;gt;, Bytes&amp;lt;32&amp;gt;) {
  let secret = getSecret();
  let context = getContext();
  let leaf = poseidonHash(secret);
  let computed_root = leaf;
  let siblings = getSiblings();
  let indices = getPathIndices();

  for (i in 0..20) {
    computed_root = hashLevelNode(indices[i], computed_root, siblings[i]);
  }

  assert(computed_root == merkle_root.read(), "Invalid membership proof");

  let nullifier = persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([secret, context]);
  assert(not used_nullifiers.contains(nullifier), "Nullifier already used");

  (computed_root, nullifier)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Admin Root Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export circuit setRoot(new_root: Bytes&amp;lt;32&amp;gt;): [] {
  let admin_secret = getAdminSecret();
  let commitment = poseidonHash(admin_secret);
  assert(commitment == admin_commitment.read(), "Not authorized");
  merkle_root.write(disclose(new_root));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The TypeScript Tooling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sparse Merkle Tree Implementation
&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MerkleTree&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;leaves&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HashHex&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&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;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HashHex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&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;Map&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;zeroHashes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HashHex&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zeroHashes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computeZeroHashes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;Map&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="nf"&gt;insertLeaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leafHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HashHex&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&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;leafIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;leaves&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leafHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setNode&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="nx"&gt;leafIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leafHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;leafIndex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="o"&gt;++&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;parentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;leftChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&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;rightChild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hashNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;leftChild&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rightChild&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&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="nx"&gt;parentIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parentHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;currentIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parentIndex&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;leafIndex&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;h2&gt;
  
  
  The Complete Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Admin Sets Up the Contract
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ADMIN_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ADMIN_COMMITMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$ADMIN_SECRET&lt;/span&gt; | poseidon-hash&lt;span class="si"&gt;)&lt;/span&gt;
compact deploy &lt;span class="nt"&gt;--ledger&lt;/span&gt; &lt;span class="nv"&gt;admin_commitment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ADMIN_COMMITMENT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add Members Off-Chain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;midnight-allowlist add-member &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="s2"&gt;"alice-secret-123"&lt;/span&gt;
&lt;span class="nv"&gt;ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;midnight-allowlist get-root&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Push Root On-Chain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact call setRoot &lt;span class="nt"&gt;--arg&lt;/span&gt; &lt;span class="nv"&gt;new_root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ROOT&lt;/span&gt; &lt;span class="nt"&gt;--witness&lt;/span&gt; &lt;span class="nv"&gt;admin_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$ADMIN_SECRET&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Member Generates and Submits Proof
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PROOF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;midnight-allowlist generate-proof &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="s2"&gt;"alice-secret-123"&lt;/span&gt; &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="s2"&gt;"voting-round-1"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
compact call proveMembership &lt;span class="nt"&gt;--proof&lt;/span&gt; &lt;span class="nv"&gt;$PROOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Edge Cases and Gotchas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Zero Hash Collisions
&lt;/h3&gt;

&lt;p&gt;The sparse tree uses pre-computed zero hashes. Make sure your &lt;code&gt;computeZeroHashes&lt;/code&gt; function matches exactly what the Compact contract expects.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Context Binding
&lt;/h3&gt;

&lt;p&gt;The nullifier is &lt;code&gt;hash(secret || context)&lt;/code&gt;. Use distinct contexts for different operations:&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;VOTE_CONTEXT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;governance-vote-q2-2026&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;AIRDROP_CONTEXT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;token-airdrop-genesis&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;h3&gt;
  
  
  3. Tree Capacity Planning
&lt;/h3&gt;

&lt;p&gt;A depth-20 tree supports ~1M members. Each additional level doubles capacity but increases proof generation time linearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;ZK Allowlist&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="s1"&gt;should verify valid membership proof&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;tree&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;MerkleTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertLeaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hashLeaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alice-secret&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;proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateMerkleProof&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="nf"&gt;verifyProof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;hashLeaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alice-secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;toBe&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This system handles the core membership proof flow. Production deployments should consider batch root updates, Merkle tree snapshots, circuit optimization, and frontend integration.&lt;/p&gt;

&lt;p&gt;The complete source code is available in the companion repository linked in the PR.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This tutorial is part of the Midnight Network bounty program. For more developer resources, visit &lt;a href="https://docs.midnight.network" rel="noopener noreferrer"&gt;docs.midnight.network&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>When Proofs Fail: A Deep Dive into Debugging Midnight Proof Server Errors</title>
      <dc:creator>BossChaos</dc:creator>
      <pubDate>Sat, 02 May 2026 12:36:52 +0000</pubDate>
      <link>https://forem.com/bosschaos/when-proofs-fail-a-deep-dive-into-debugging-midnight-proof-server-errors-15kg</link>
      <guid>https://forem.com/bosschaos/when-proofs-fail-a-deep-dive-into-debugging-midnight-proof-server-errors-15kg</guid>
      <description>&lt;h1&gt;
  
  
  When Proofs Fail: A Deep Dive into Debugging Midnight Proof Server Errors
&lt;/h1&gt;

&lt;p&gt;Your contract compiles. The witness generates correctly. But &lt;code&gt;proveTx&lt;/code&gt; hangs for five minutes and throws an error you've never seen before. Welcome to the world of ZK proof generation — where the gap between "should work" and "does work" is measured in docker logs, wire format mismatches, and version drift.&lt;/p&gt;

&lt;p&gt;I've spent the last few months debugging proof server failures across multiple Midnight dApp projects. Most tutorials tell you to "check &lt;code&gt;docker logs&lt;/code&gt;" and leave it at that. This one goes deeper — into the SDK source code, the HTTP protocol between your app and the proof server, and the exact failure modes that cause proofs to silently reject or timeout.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Proof Server Actually Works (The Architecture You Need to Understand)
&lt;/h2&gt;

&lt;p&gt;Before debugging, you need to understand the request flow. The Midnight SDK doesn't use a single &lt;code&gt;/prove-tx&lt;/code&gt; endpoint. Instead, it breaks proving into two HTTP calls against the proof server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your dApp
    │
    │ httpClientProofProvider(url, zkConfigProvider)
    │
    ├─→ POST /check   (validates circuit preimage)
    │       ↓
    │    Returns constraint check results
    │
    └─→ POST /prove   (generates the actual ZK proof)
            ↓
         Returns proof bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the &lt;a href="https://github.com/midnightntwrk/midnight-js/blob/main/packages/http-client-proof-provider/src/http-client-proof-provider.ts" rel="noopener noreferrer"&gt;&lt;code&gt;httpClientProofProvider&lt;/code&gt;&lt;/a&gt; source code, here's the critical part:&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;retryOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;retryDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// 2s, 4s, 8s&lt;/span&gt;
  &lt;span class="na"&gt;retryOn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;503&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;The SDK &lt;strong&gt;automatically retries&lt;/strong&gt; on HTTP 500 and 503 errors with exponential backoff. If you see a request taking 14+ seconds and then failing, that's not your code — that's three retries timing out.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Handling Midnight SDK Breaking Changes: A Developer's Upgrade Playbook</title>
      <dc:creator>BossChaos</dc:creator>
      <pubDate>Sat, 02 May 2026 09:32:06 +0000</pubDate>
      <link>https://forem.com/bosschaos/handling-midnight-sdk-breaking-changes-a-developers-upgrade-playbook-116o</link>
      <guid>https://forem.com/bosschaos/handling-midnight-sdk-breaking-changes-a-developers-upgrade-playbook-116o</guid>
      <description></description>
      <category>web3</category>
    </item>
    <item>
      <title>Compact Standard Library: A Practical Guide to Every Export</title>
      <dc:creator>BossChaos</dc:creator>
      <pubDate>Sat, 02 May 2026 04:24:22 +0000</pubDate>
      <link>https://forem.com/bosschaos/compact-standard-library-a-practical-guide-to-every-export-11dh</link>
      <guid>https://forem.com/bosschaos/compact-standard-library-a-practical-guide-to-every-export-11dh</guid>
      <description>&lt;h1&gt;
  
  
  Compact Standard Library: A Practical Guide to Every Export
&lt;/h1&gt;

&lt;p&gt;The Compact standard library ships with every Midnight project — you don't install it, you don't import a package manager dependency. It's just there, baked into the compiler. But the official docs are sparse enough that most developers only discover what's available by reading other people's contracts or stumbling into a type error.&lt;/p&gt;

&lt;p&gt;This guide fixes that. I'll walk through every meaningful export, grouped by what it actually does, with working code examples you can compile and test. The companion repository contains all the contracts as individual &lt;code&gt;.compact&lt;/code&gt; files, ready to compile with &lt;code&gt;compactc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you've been writing Compact contracts and keep wondering "is there a built-in way to do this?", the answer is probably in here.&lt;/p&gt;




&lt;h2&gt;
  
  
  Generic Types: Maybe and Either
&lt;/h2&gt;

&lt;p&gt;These two show up in nearly every non-trivial contract. If you've written TypeScript or Rust, the concepts are familiar — but the ZK context adds some nuances that matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maybe&amp;lt;T&amp;gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Maybe&amp;lt;T&amp;gt;&lt;/code&gt; represents an optional value: either &lt;code&gt;Some(value)&lt;/code&gt; or &lt;code&gt;None&lt;/code&gt;. No nulls, no sentinel values like &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;-1&lt;/code&gt; pretending to mean "not set."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract MaybeExample {
  ledger admin: Maybe&amp;lt;ContractAddress&amp;gt;;
  ledger config: Maybe&amp;lt;Uint&amp;lt;64&amp;gt;&amp;gt;;

  circuit setAdmin(addr: ContractAddress): [] {
    assert ledger.admin.isNone : "admin already set";
    ledger.admin = Maybe.some(addr);
  }

  circuit clearAdmin(): [] {
    assert ledger.admin.isSome : "no admin to clear";
    ledger.admin = Maybe.none();
  }

  circuit isAdmin(addr: ContractAddress): [Boolean] {
    const current = ledger.admin;
    return [current.isSome &amp;amp;&amp;amp; current.value == addr];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.isSome&lt;/code&gt;&lt;/strong&gt; — Boolean, &lt;code&gt;true&lt;/code&gt; if the value exists&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.isNone&lt;/code&gt;&lt;/strong&gt; — Boolean, &lt;code&gt;true&lt;/code&gt; if the value is absent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.value&lt;/code&gt;&lt;/strong&gt; — The inner value of type &lt;code&gt;T&lt;/code&gt;, but &lt;strong&gt;only safe to access when you know it's &lt;code&gt;Some&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the critical gotcha: accessing &lt;code&gt;.value&lt;/code&gt; on a &lt;code&gt;None&lt;/code&gt; inside a ZK circuit doesn't throw an exception — it makes the proof impossible to generate. The constraint solver hits an unsatisfiable condition and fails silently. Always gate &lt;code&gt;.value&lt;/code&gt; access behind an &lt;code&gt;.isSome&lt;/code&gt; check.&lt;/p&gt;

&lt;p&gt;A common pattern is lazy initialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit initConfig(value: Uint&amp;lt;64&amp;gt;): [] {
  if ledger.config.isNone {
    ledger.config = Maybe.some(value);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Either&amp;lt;L, R&amp;gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Either&amp;lt;L, R&amp;gt;&lt;/code&gt; is a tagged union — one of two possible types, &lt;code&gt;Left(L)&lt;/code&gt; or &lt;code&gt;Right(R)&lt;/code&gt;. By convention, &lt;code&gt;Left&lt;/code&gt; carries error information and &lt;code&gt;Right&lt;/code&gt; carries success values. Compact doesn't enforce this convention, but following it makes your code readable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract EitherDemo {
  circuit safeDivide(
    witness dividend: Uint&amp;lt;64&amp;gt;,
    witness divisor: Uint&amp;lt;64&amp;gt;
  ): [Either&amp;lt;Bytes&amp;lt;32&amp;gt;, Uint&amp;lt;64&amp;gt;&amp;gt;] {
    if divisor == Uint&amp;lt;64&amp;gt;::from(0) {
      return [Either.left(bytes("division by zero"))];
    }
    return [Either.right(dividend / divisor)];
  }

  circuit processResult(
    witness a: Uint&amp;lt;64&amp;gt;,
    witness b: Uint&amp;lt;64&amp;gt;
  ): [Boolean, Uint&amp;lt;64&amp;gt;] {
    const result = EitherDemo::safeDivide(a, b);
    if result.isLeft {
      return [false, Uint&amp;lt;64&amp;gt;::from(0)];
    }
    return [true, result.rightValue];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Discriminate with &lt;code&gt;.isLeft&lt;/code&gt;, &lt;code&gt;.isRight&lt;/code&gt;, &lt;code&gt;.leftValue&lt;/code&gt;, and &lt;code&gt;.rightValue&lt;/code&gt;. Same caveat as &lt;code&gt;Maybe&lt;/code&gt; — accessing the wrong branch causes a constraint failure, not a recoverable error.&lt;/p&gt;




&lt;h2&gt;
  
  
  Merkle Trees and Commitments
&lt;/h2&gt;

&lt;p&gt;Merkle trees are the backbone of ZK contract design. The standard library gives you the tree type itself plus the commitment and verification primitives you need for privacy-preserving state proofs.&lt;/p&gt;

&lt;h3&gt;
  
  
  MerkleTree&amp;lt;N, T&amp;gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;MerkleTree&amp;lt;N, T&amp;gt;&lt;/code&gt; is a complete binary Merkle tree with &lt;code&gt;N&lt;/code&gt; levels — meaning &lt;code&gt;2^N&lt;/code&gt; leaves — holding values of type &lt;code&gt;T&lt;/code&gt;. The size is fixed at compile time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract MerkleRegistry {
  export ledger members: MerkleTree&amp;lt;20, Bytes&amp;lt;32&amp;gt;&amp;gt;;
  export ledger memberCount: Uint&amp;lt;64&amp;gt;;

  circuit addMember(commitment: Bytes&amp;lt;32&amp;gt;): [Uint&amp;lt;64&amp;gt;] {
    const index = ledger.memberCount;
    ledger.members.set(index, commitment);
    ledger.memberCount = index + Uint&amp;lt;64&amp;gt;::from(1);
    return [index];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plan your tree depth early. &lt;code&gt;N=16&lt;/code&gt; gives you 65,536 entries (fine for small registries). &lt;code&gt;N=20&lt;/code&gt; gives you ~1 million (good for most production use cases). &lt;code&gt;N=32&lt;/code&gt; gives you 4 billion (overkill unless you're building something at scale).&lt;/p&gt;

&lt;p&gt;The tree supports &lt;code&gt;.root()&lt;/code&gt; to get the current root hash and &lt;code&gt;.set(index, value)&lt;/code&gt; to update a leaf. You can verify membership with &lt;code&gt;verifyMerkleProof(leaf, root, pathElements, pathIndices)&lt;/code&gt; — this is how you prove someone is in the registry without revealing who else is.&lt;/p&gt;

&lt;h3&gt;
  
  
  persistentCommit
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;persistentCommit(witness: Bytes&amp;lt;32&amp;gt;)&lt;/code&gt; creates a commitment anchored to Midnight's state tree. It's deterministic — the same secret always produces the same commitment — and it's tied to a specific epoch, enabling "this value existed at this point in time" proofs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit commit(witness secret: Bytes&amp;lt;32&amp;gt;): [Bytes&amp;lt;32&amp;gt;] {
  const commitment = persistentCommit(secret);
  ledger.commitments.set(index, commitment);
  return [commitment];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the foundation of commit-reveal patterns. Commit on-chain now, reveal off-chain later when you're ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  verifyCommitment
&lt;/h3&gt;

&lt;p&gt;The counterpart to &lt;code&gt;persistentCommit&lt;/code&gt;: given a secret and a commitment, verify they match.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit reveal(
  witness secret: Bytes&amp;lt;32&amp;gt;,
  public commitment: Bytes&amp;lt;32&amp;gt;
): [Boolean] {
  return [verifyCommitment(secret, commitment)];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together, &lt;code&gt;persistentCommit&lt;/code&gt; and &lt;code&gt;verifyCommitment&lt;/code&gt; power sealed-bid auctions, private voting, and nullifier-based replay prevention.&lt;/p&gt;




&lt;h2&gt;
  
  
  Elliptic Curves
&lt;/h2&gt;

&lt;p&gt;Midnight's ZK circuits operate over elliptic curves under the hood. The standard library surfaces some of this for contracts that need explicit cryptographic operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  CurvePoint
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CurvePoint&lt;/code&gt; represents a point on Midnight's elliptic curve. You interact with these when doing key derivation, signature verification, or building custom commitment schemes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit derivePublicKey(
  witness privateKey: Scalar
): [CurvePoint] {
  const G = CurvePoint.generator();
  return [G.multiply(privateKey)];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.generator()&lt;/code&gt;&lt;/strong&gt; — Static method, returns the base point &lt;code&gt;G&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.multiply(scalar)&lt;/code&gt;&lt;/strong&gt; — Scalar multiplication (expensive in ZK)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.add(other)&lt;/code&gt;&lt;/strong&gt; — Point addition (cheaper)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.negate()&lt;/code&gt;&lt;/strong&gt; — Point negation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.hashToCurve(bytes)&lt;/code&gt;&lt;/strong&gt; — Hash arbitrary data to a curve point&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scalar
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Scalar&lt;/code&gt; is a field element in the elliptic curve's scalar field. It's what you use for private keys, blinding factors, and random nonces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit pedersenCommit(
  witness value: Uint&amp;lt;64&amp;gt;,
  witness blinding: Scalar
): [CurvePoint] {
  const H = CurvePoint.hashToCurve(bytes("value"));
  const G = CurvePoint.generator();
  return [H.multiply(Scalar.fromUint(value)).add(G.multiply(blinding))];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pedersen commitment pattern above is worth knowing: it commits to a value while keeping it hidden, and the commitments are additively homomorphic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Kernel Types
&lt;/h2&gt;

&lt;p&gt;These types bridge the ZK contract world and Midnight's transaction kernel. They're how your contract talks about identity, addresses, and shielded coin state.&lt;/p&gt;

&lt;h3&gt;
  
  
  ContractAddress
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ContractAddress&lt;/code&gt; is the canonical identity for smart contracts. It's derived from the contract's code hash and deployment parameters — deterministic and unique.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract AccessControlled {
  export ledger admin: ContractAddress;

  circuit initialize(adminAddr: ContractAddress): [] {
    assert ledger.admin == ContractAddress.zero() : "already initialized";
    ledger.admin = adminAddr;
  }

  circuit adminAction(): [] {
    assert ledger.admin == ContractAddress.self() : "unauthorized";
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.zero()&lt;/code&gt;&lt;/strong&gt; — Returns the zero address (the default/unset value)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.self()&lt;/code&gt;&lt;/strong&gt; — Returns this contract's own address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;code&gt;ContractAddress&lt;/code&gt; for identity-based access control. Never use &lt;code&gt;ownPublicKey()&lt;/code&gt; for this purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  ZswapCoinPublicKey
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ZswapCoinPublicKey&lt;/code&gt; is the public key type for Zswap coin ownership — Midnight's shielded asset system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ledger depositor: ZswapCoinPublicKey;

circuit recordDepositor(pk: ZswapCoinPublicKey): [] {
  ledger.depositor = pk;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UserAddress
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;UserAddress&lt;/code&gt; is a higher-level address type that wraps both a &lt;code&gt;ZswapCoinPublicKey&lt;/code&gt; and a spending key derivation path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit getRecipientKey(userAddr: UserAddress): [ZswapCoinPublicKey] {
  return [userAddr.spendingKey()];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ShieldedCoinInfo
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ShieldedCoinInfo&lt;/code&gt; describes a shielded coin: its value, token type, and randomness.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit inspectCoin(coinInfo: ShieldedCoinInfo): [Uint&amp;lt;64&amp;gt;, Bytes&amp;lt;32&amp;gt;] {
  return [coinInfo.value, coinInfo.tokenType];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  QualifiedShieldedCoinInfo
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;QualifiedShieldedCoinInfo&lt;/code&gt; extends &lt;code&gt;ShieldedCoinInfo&lt;/code&gt; with the nullifier key needed to spend the coin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit deposit(witness coinProof: CoinProof): [QualifiedShieldedCoinInfo] {
  const coin = receiveShielded(coinProof);
  return [coin];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CoinProof
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CoinProof&lt;/code&gt; is the ZK proof that demonstrates ownership of a shielded coin without revealing the coin's details.&lt;/p&gt;




&lt;h2&gt;
  
  
  Helper Circuits
&lt;/h2&gt;

&lt;p&gt;These are the high-level operations for working with tokens and shielded transfers.&lt;/p&gt;

&lt;h3&gt;
  
  
  nativeToken()
&lt;/h3&gt;

&lt;p&gt;Returns the token type identifier for MNT, Midnight's native token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit acceptMNTOnly(coinType: Bytes&amp;lt;32&amp;gt;): [] {
  assert coinType == nativeToken() : "only MNT accepted";
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  tokenType(ContractAddress)
&lt;/h3&gt;

&lt;p&gt;Derives the token type identifier for a custom token contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit isMyToken(coinType: Bytes&amp;lt;32&amp;gt;): [Boolean] {
  return [coinType == tokenType(ContractAddress.self())];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  evolveNonce
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;evolveNonce(nonce: Bytes&amp;lt;32&amp;gt;)&lt;/code&gt; advances a nonce to prevent replay attacks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit useNonce(witness action: Bytes&amp;lt;32&amp;gt;): [Bytes&amp;lt;32&amp;gt;] {
  const nonce = ledger.currentNonce;
  const nextNonce = evolveNonce(nonce);
  ledger.currentNonce = nextNonce;
  assert verifyCommitment(action, nonce) : "invalid nonce";
  return [nextNonce];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  shieldedBurnAddress()
&lt;/h3&gt;

&lt;p&gt;Returns the canonical burn address for Midnight.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit burn(amount: Uint&amp;lt;64&amp;gt;): [] {
  sendShielded(shieldedBurnAddress(), amount);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Shielded Token Operations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  receiveShielded
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;receiveShielded(coinProof: CoinProof)&lt;/code&gt; processes an incoming shielded coin transfer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit deposit(witness coinProof: CoinProof): [QualifiedShieldedCoinInfo] {
  const coin = receiveShielded(coinProof);
  assert coin.info.tokenType == nativeToken() : "MNT only";
  ledger.balance = ledger.balance + coin.info.value;
  return [coin];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  sendShielded
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sendShielded(recipient: ZswapCoinPublicKey, coin: QualifiedShieldedCoinInfo, amount: Uint&amp;lt;64&amp;gt;)&lt;/code&gt; constructs and sends a shielded transfer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;circuit withdraw(
  recipient: ZswapCoinPublicKey,
  amount: Uint&amp;lt;64&amp;gt;
): [] {
  assert amount &amp;lt;= ledger.balance : "insufficient balance";
  ledger.balance = ledger.balance - amount;
  sendShielded(recipient, ledger.heldCoin, amount);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Block-Time Queries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  getBlockTime()
&lt;/h3&gt;

&lt;p&gt;Returns the current block timestamp as a &lt;code&gt;Uint&amp;lt;64&amp;gt;&lt;/code&gt; (Unix epoch seconds).&lt;/p&gt;

&lt;h3&gt;
  
  
  getBlockNumber()
&lt;/h3&gt;

&lt;p&gt;Returns the current block height as a &lt;code&gt;Uint&amp;lt;64&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  getEpoch()
&lt;/h3&gt;

&lt;p&gt;Returns the current epoch number as a &lt;code&gt;Uint&amp;lt;64&amp;gt;&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;contract TimeEscrow {
  export ledger unlockBlock: Uint&amp;lt;64&amp;gt;;
  export ledger locked: Boolean;

  circuit lock(duration: Uint&amp;lt;64&amp;gt;): [] {
    assert !ledger.locked : "already locked";
    ledger.unlockBlock = getBlockNumber() + duration;
    ledger.locked = true;
  }

  circuit release(): [] {
    assert getBlockNumber() &amp;gt;= ledger.unlockBlock : "still locked";
    ledger.locked = false;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;getBlockNumber()&lt;/code&gt; for relative timing, &lt;code&gt;getBlockTime()&lt;/code&gt; for absolute deadlines, and &lt;code&gt;getEpoch()&lt;/code&gt; for rate-limiting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting It All Together: A Token Vault
&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.22;

contract TokenVault {
  export ledger deposits: MerkleTree&amp;lt;16, Bytes&amp;lt;32&amp;gt;&amp;gt;;
  export ledger depositCount: Uint&amp;lt;64&amp;gt;;
  export ledger totalBalance: Uint&amp;lt;64&amp;gt;;
  export ledger heldCoins: MerkleTree&amp;lt;16, QualifiedShieldedCoinInfo&amp;gt;;

  circuit deposit(witness coinProof: CoinProof): [Uint&amp;lt;64&amp;gt;] {
    const coin = receiveShielded(coinProof);
    assert coin.info.tokenType == nativeToken() : "MNT only";

    const index = ledger.depositCount;
    ledger.heldCoins.set(index, coin);
    ledger.totalBalance = ledger.totalBalance + coin.info.value;
    ledger.depositCount = index + Uint&amp;lt;64&amp;gt;::from(1);

    const commitment = persistentCommit(
      coin.info.value.toBytes().concat(index.toBytes())
    );
    ledger.deposits.set(index, commitment);

    return [index];
  }

  circuit withdraw(
    coinIndex: Uint&amp;lt;64&amp;gt;,
    recipient: ZswapCoinPublicKey
  ): [] {
    const coin = ledger.heldCoins.get(coinIndex);
    assert coin.isSome : "coin not found";
    ledger.totalBalance = ledger.totalBalance - coin.value.info.value;
    sendShielded(recipient, coin.value, coin.value.info.value);
    ledger.heldCoins.set(coinIndex, Maybe.none());
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Export&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&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;Generic&lt;/td&gt;
&lt;td&gt;Optional values, nullable ledger fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Either&amp;lt;L, R&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generic&lt;/td&gt;
&lt;td&gt;Two-outcome results, structured errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MerkleTree&amp;lt;N, T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merkle&lt;/td&gt;
&lt;td&gt;Fixed-size on-chain indexed storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;persistentCommit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merkle&lt;/td&gt;
&lt;td&gt;Creating verifiable commitments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verifyCommitment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merkle&lt;/td&gt;
&lt;td&gt;Verifying commitments in proofs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;verifyMerkleProof&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merkle&lt;/td&gt;
&lt;td&gt;Proving leaf membership in a tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CurvePoint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Crypto&lt;/td&gt;
&lt;td&gt;Key derivation, Pedersen commitments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Scalar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Crypto&lt;/td&gt;
&lt;td&gt;Private keys, blinding factors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ContractAddress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;Contract identity, access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ZswapCoinPublicKey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;Shielded coin recipient keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserAddress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;User-facing addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ShieldedCoinInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;Coin value/type metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;QualifiedShieldedCoinInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;Spendable coin with nullifier key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CoinProof&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kernel&lt;/td&gt;
&lt;td&gt;ZK proof of coin ownership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nativeToken()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Helper&lt;/td&gt;
&lt;td&gt;MNT token type identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tokenType(addr)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Helper&lt;/td&gt;
&lt;td&gt;Custom token type identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;evolveNonce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Helper&lt;/td&gt;
&lt;td&gt;Replay-protected nonce chaining&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shieldedBurnAddress()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Helper&lt;/td&gt;
&lt;td&gt;Canonical token burn address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;receiveShielded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transfer&lt;/td&gt;
&lt;td&gt;Accept incoming shielded coins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sendShielded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transfer&lt;/td&gt;
&lt;td&gt;Send shielded coins to recipient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getBlockTime()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;Current block timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getBlockNumber()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;Current block height&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getEpoch()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;Current consensus epoch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;The Compact standard library is deliberately minimal. Midnight's philosophy is to give you the cryptographic building blocks and let you compose them.&lt;/p&gt;

&lt;p&gt;The types you'll reach for in every contract: &lt;code&gt;Maybe&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;ContractAddress&lt;/code&gt;, &lt;code&gt;receiveShielded&lt;/code&gt;, &lt;code&gt;sendShielded&lt;/code&gt;. Everything else is situational.&lt;/p&gt;

&lt;p&gt;The biggest gotcha for newcomers: all of these operations happen inside ZK circuits. "Optional" doesn't mean "try/catch" — it means "the proof is either valid or it isn't."&lt;/p&gt;

&lt;p&gt;All code examples are available as individual &lt;code&gt;.compact&lt;/code&gt; files in the companion repository:&lt;br&gt;
&lt;a href="https://github.com/BossChaos/contributor-hub/tree/bounty/293-compact-stdlib/bounties/293-compact-stdlib" rel="noopener noreferrer"&gt;https://github.com/BossChaos/contributor-hub/tree/bounty/293-compact-stdlib/bounties/293-compact-stdlib&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  MidnightforDevs #Compact #ZK #Privacy #Blockchain
&lt;/h1&gt;

</description>
      <category>privacy</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
