<?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: Elliot lucky</title>
    <description>The latest articles on DEV Community by Elliot lucky (@codebigint_01).</description>
    <link>https://dev.to/codebigint_01</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%2F1706891%2Feeaef3a0-80c9-4d44-9af3-3ae6d6d6f6a9.jpg</url>
      <title>DEV Community: Elliot lucky</title>
      <link>https://dev.to/codebigint_01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codebigint_01"/>
    <language>en</language>
    <item>
      <title>Build a Private Vault DApp Smart Contract on Midnight with Compact</title>
      <dc:creator>Elliot lucky</dc:creator>
      <pubDate>Mon, 27 Apr 2026 01:25:37 +0000</pubDate>
      <link>https://dev.to/codebigint_01/build-a-private-vault-dapp-smart-contract-on-midnight-with-compact-53od</link>
      <guid>https://dev.to/codebigint_01/build-a-private-vault-dapp-smart-contract-on-midnight-with-compact-53od</guid>
      <description>&lt;p&gt;In this guide, we will build a vault dApp smart contract from scratch using Compact on Midnight.&lt;/p&gt;

&lt;p&gt;The goal is to help developers who already know the basics of Compact start writing a complete smart contract that handles real vault behavior: receiving shielded tokens, tracking user vault balances, accumulating multiple deposits, and withdrawing funds.&lt;/p&gt;

&lt;p&gt;We will focus on the contract side of the dApp. There is no frontend in this guide. The point is to understand the smart contract mechanics clearly before adding UI complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;We are building a shielded token vault contract where a user can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deposit a shielded coin into the vault&lt;/li&gt;
&lt;li&gt;automatically create a vault on the first deposit&lt;/li&gt;
&lt;li&gt;keep later deposits accumulated in the same vault&lt;/li&gt;
&lt;li&gt;reject deposits with the wrong coin color&lt;/li&gt;
&lt;li&gt;withdraw from the vault&lt;/li&gt;
&lt;li&gt;keep the contract's shielded UTXO balance updated manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://github.com/codeBigInt/midnight-simplified-tutorial-dapps/tree/main/vault-dapp" rel="noopener noreferrer"&gt;final project&lt;/a&gt; has this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault-dapp/
|-- package.json
|-- src/
|   |-- vault.compact
|   |-- witness.ts
|   |-- managed/
|   `-- test/
|       |-- vault.test.ts
|       |-- vault-setup.ts
|       `-- utils.ts
`-- tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core files are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/vault.compact&lt;/code&gt;: the Compact smart contract&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/witness.ts&lt;/code&gt;: the TypeScript witness implementation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/test/vault-setup.ts&lt;/code&gt;: the simulator setup&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/test/vault.test.ts&lt;/code&gt;: the behavior tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;To follow along, you should already have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your Midnight developer environment set up&lt;/li&gt;
&lt;li&gt;the Compact toolchain installed&lt;/li&gt;
&lt;li&gt;basic Compact knowledge&lt;/li&gt;
&lt;li&gt;Bun installed&lt;/li&gt;
&lt;li&gt;a code editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure your Compact compiler version matches the language version used in this project. This contract was built against Compact compiler &lt;code&gt;0.31.0&lt;/code&gt; and language version &lt;code&gt;0.23.0&lt;/code&gt;, so the pragma is pinned:&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.23.0;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pinning the pragma avoids drift when newer compiler versions introduce language changes.&lt;/p&gt;

&lt;p&gt;You will also want the &lt;a href="https://docs.midnight.network/compact/standard-library/exports#receiveshielded" rel="noopener noreferrer"&gt;Compact standard library&lt;/a&gt; and &lt;a href="https://docs.midnight.network/compact/data-types/ledger-adt" rel="noopener noreferrer"&gt;Ledger data type&lt;/a&gt; documentation open because we be will using the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#receiveshielded" rel="noopener noreferrer"&gt;&lt;code&gt;receiveShielded&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#sendshielded" rel="noopener noreferrer"&gt;&lt;code&gt;sendShielded&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#mergecoinimmediate" rel="noopener noreferrer"&gt;&lt;code&gt;mergeCoinImmediate&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/compact/data-types/ledger-adt#insertcoin-1" rel="noopener noreferrer"&gt;&lt;code&gt;insertCoin&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#ownpublickey" rel="noopener noreferrer"&gt;&lt;code&gt;ownPublicKey&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/compact/data-types/ledger-adt#self" rel="noopener noreferrer"&gt;&lt;code&gt;kernel.self&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create the Project
&lt;/h2&gt;

&lt;p&gt;Create the folder:&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;mkdir &lt;/span&gt;vault-dapp
&lt;span class="nb"&gt;cd &lt;/span&gt;vault-dapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize the project with Bun:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose the blank template. Bun will create the initial &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then create the source folder and the Compact file:&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;mkdir &lt;/span&gt;src
&lt;span class="nb"&gt;touch &lt;/span&gt;src/vault.compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add Compile Scripts
&lt;/h2&gt;

&lt;p&gt;Before writing the full contract, add scripts so you do not have to keep typing the Compact compiler command manually.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;package.json&lt;/code&gt;, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test-compile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"compact compile --skip-zk src/vault.compact ./src/managed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"compile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"compact compile src/vault.compact ./src/managed"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;test-compile&lt;/code&gt; script compiles the Compact code without generating the full zero-knowledge materials. That makes it useful while you are actively developing.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;compile&lt;/code&gt; script performs the full compile.&lt;/p&gt;

&lt;p&gt;Both scripts write generated artifacts into:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If that folder does not exist, the compiler creates it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Start the Compact Contract
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;src/vault.compact&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first line is the pinned language pragma:&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.23.0;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then import the standard library:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The standard library gives us access to the built-in circuits and types we need for token transfer, ledger helpers, hashing, and shielded coin management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add the Main Circuit Skeleton
&lt;/h2&gt;

&lt;p&gt;The vault has two exported circuits:&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 deposit(
    _coin: ShieldedCoinInfo
): []{
}

export circuit withdraw(_amount: Uint&amp;lt;128&amp;gt;): []{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the user-facing actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deposit&lt;/code&gt;: deposit shielded tokens into the contract, creating the user's vault on the first deposit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;withdraw&lt;/code&gt;: withdraw shielded tokens from the contract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run a quick compile to confirm the setup works:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If the setup is correct, Compact generates the managed TypeScript artifacts in &lt;code&gt;src/managed&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Define the Vault Struct
&lt;/h2&gt;

&lt;p&gt;Now we define the shape of a vault position:&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 Vault {
    balance: Uint&amp;lt;128&amp;gt;;
    coinColor: Bytes&amp;lt;32&amp;gt;;
    ownerHash: Bytes&amp;lt;32&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vault stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;balance&lt;/code&gt;: the amount deposited into the vault&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;coinColor&lt;/code&gt;: the token color this vault accepts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ownerHash&lt;/code&gt;: a commitment to the public key that owns the vault&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Midnight, token types are identified by a byte string called a color. Different assets have different colors, so we use &lt;code&gt;coinColor&lt;/code&gt; to make sure a user keeps one vault position tied to one asset type.&lt;/p&gt;

&lt;p&gt;The final code uses &lt;code&gt;Uint&amp;lt;128&amp;gt;&lt;/code&gt; for &lt;code&gt;balance&lt;/code&gt; because &lt;code&gt;ShieldedCoinInfo.value&lt;/code&gt; is also &lt;code&gt;Uint&amp;lt;128&amp;gt;&lt;/code&gt;. If you use &lt;code&gt;Uint&amp;lt;64&amp;gt;&lt;/code&gt; for &lt;code&gt;balance&lt;/code&gt;, the compiler will complain when you add a coin value to the vault balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy Trade-Off
&lt;/h2&gt;

&lt;p&gt;This tutorial keeps the contract intentionally small, but the design has an important privacy trade-off.&lt;/p&gt;

&lt;p&gt;The shielded coin itself is private, and token movement uses &lt;code&gt;receiveShielded&lt;/code&gt; and &lt;code&gt;sendShielded&lt;/code&gt;. However, each &lt;code&gt;Vault.balance&lt;/code&gt; is stored as a plain &lt;code&gt;Uint&amp;lt;128&amp;gt;&lt;/code&gt; in a public &lt;code&gt;Map&lt;/code&gt;, and each vault also stores the accepted &lt;code&gt;coinColor&lt;/code&gt;. That means the contract publicly exposes each vault's running accounting balance and asset color, even though the underlying coin is shielded.&lt;/p&gt;

&lt;p&gt;This is acceptable for a learning example focused on shielded token handling and UTXO management. A production privacy-first vault should usually keep per-user balances in private state and store only commitments on-chain, for example with a Merkle-tree based design that proves balance updates without exposing each user's balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Add Ledger State
&lt;/h2&gt;

&lt;p&gt;We need one ledger map for vault positions:&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 vaults: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Vault&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This maps a generated user ID to a vault.&lt;/p&gt;

&lt;p&gt;We also need another map to manage shielded coin balances:&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 balances: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, QualifiedShieldedCoinInfo&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This map stores contract-held shielded coins by coin color.&lt;/p&gt;

&lt;p&gt;This part is important: when you deal with shielded tokens, the contract does not automatically maintain an easy balance table for you. You receive the shielded coin, but you still need to manage the resulting UTXO state yourself. That is why the &lt;code&gt;balances&lt;/code&gt; map exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  ShieldedCoinInfo vs QualifiedShieldedCoinInfo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#shieldedcoininfo" rel="noopener noreferrer"&gt;&lt;code&gt;ShieldedCoinInfo&lt;/code&gt;&lt;/a&gt; represents a newly created shielded coin used when receiving or spending a coin in a transaction. It has fields such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nonce&lt;/li&gt;
&lt;li&gt;color&lt;/li&gt;
&lt;li&gt;value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#qualifiedshieldedcoininfo" rel="noopener noreferrer"&gt;&lt;code&gt;QualifiedShieldedCoinInfo&lt;/code&gt;&lt;/a&gt; represents a shielded coin that already exists on the ledger and is ready to be spent. It includes the extra information needed to locate it, such as the Merkle tree index.&lt;/p&gt;

&lt;p&gt;That is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deposits accept &lt;code&gt;ShieldedCoinInfo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;contract balances are stored as &lt;code&gt;QualifiedShieldedCoinInfo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;insertCoin&lt;/code&gt; converts the received shielded coin into the qualified form stored in the ledger&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 7: Add the Witness Function
&lt;/h2&gt;

&lt;p&gt;We do not want to store the user's raw secret in public ledger state.&lt;/p&gt;

&lt;p&gt;Instead, we derive a user ID from a secret key stored in the user's private state.&lt;/p&gt;

&lt;p&gt;Declare the witness:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;witness getSecretKey(): Bytes&amp;lt;32&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;getSecretKey()&lt;/code&gt; lets the contract receive the user's secret key from private state during execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Generate a User ID
&lt;/h2&gt;

&lt;p&gt;Now add a helper circuit:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;persistentHash&lt;/code&gt; gives us a deterministic ID from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a fixed domain separator&lt;/li&gt;
&lt;li&gt;the user's secret key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the commitment-based per-user identity used by the vault. It is the part of the design that lets the contract track balances by a commitment derived from private witness data instead of by storing a raw user address.&lt;/p&gt;

&lt;p&gt;The current code uses &lt;code&gt;"vault:user"&lt;/code&gt; as the domain separator. For a real deployed contract, choose this string carefully before deployment because changing it later changes all derived user IDs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Add the Owner Commitment Helper
&lt;/h2&gt;

&lt;p&gt;The owner commitment stores a hash of the public key used when the vault is first created:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This is not meant to reveal the public key as a raw field in the vault. The vault stores only the commitment hash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: Implement deposit
&lt;/h2&gt;

&lt;p&gt;The user deposits a shielded coin:&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 deposit(
    _coin: ShieldedCoinInfo
): []{
    const coin = disclose(_coin);

    receiveShielded(coin);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;receiveShielded&lt;/code&gt; validates that the coin is being received by the contract in this transaction.&lt;/p&gt;

&lt;p&gt;But receiving is not enough. The contract still needs to store or update the shielded balance manually.&lt;/p&gt;

&lt;p&gt;If there is already a balance for this coin color, merge the previous ledger coin with the new coin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(balances.member(coin.color)){
    const prevBalance = balances.lookup(coin.color);
    const newBalance = mergeCoinImmediate(prevBalance, coin);

    balances.insertCoin(
        coin.color,
        newBalance,
        right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())
    );
}else{
    balances.insertCoin(
       coin.color,
       coin,
       right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why &lt;a href="https://docs.midnight.network/compact/standard-library/exports#mergecoinimmediate" rel="noopener noreferrer"&gt;&lt;code&gt;mergeCoinImmediate&lt;/code&gt;&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.midnight.network/compact/standard-library/exports#mergecoin" rel="noopener noreferrer"&gt;&lt;code&gt;mergeCoin&lt;/code&gt;&lt;/a&gt; is for merging two coins that already exist on the ledger. In this case, we are merging an existing &lt;code&gt;QualifiedShieldedCoinInfo&lt;/code&gt; with a new incoming &lt;code&gt;ShieldedCoinInfo&lt;/code&gt;, so we use &lt;code&gt;mergeCoinImmediate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why &lt;code&gt;right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The recipient type can be either a user public key or a contract address. Since the contract is storing this balance for itself, we pass the contract address using the &lt;a href="https://docs.midnight.network/compact/standard-library/exports#right" rel="noopener noreferrer"&gt;&lt;code&gt;right&lt;/code&gt;&lt;/a&gt; constructor.&lt;/p&gt;

&lt;p&gt;Next, derive the user ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const userId = generateUserId(disclose(getSecretKey()));
const hasVault = vaults.member(userId);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first deposit, create the vault:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(!hasVault){
    const ownerCommitmentHash = generateOwnerCommitment(ownPublicKey().bytes);

    const newVault = Vault {
        balance: coin.value,
        coinColor: disclose(coin.color),
        ownerHash: ownerCommitmentHash
    };

    vaults.insert(userId, newVault);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On later deposits, require the same coin color and add the new amount to the existing balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;else{
    const vault = vaults.lookup(userId);

    assert (coin.color == vault.coinColor, "Invalid coin type deposited");

    const updatedVault = Vault {
        ...vault,
        balance: (vault.balance + coin.value) as Uint&amp;lt;128&amp;gt;
    };

    vaults.insert(userId, updatedVault);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cast to &lt;code&gt;Uint&amp;lt;128&amp;gt;&lt;/code&gt; matters because &lt;code&gt;coin.value&lt;/code&gt; is &lt;code&gt;Uint&amp;lt;128&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run the compile script again:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 11: Implement withdraw
&lt;/h2&gt;

&lt;p&gt;The withdrawal circuit accepts an amount:&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 withdraw(_amount: Uint&amp;lt;128&amp;gt;): []{
    const amount = disclose(_amount);
    const userId = generateUserId(disclose(getSecretKey()));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reconstruct the owner commitment from the public key creating the transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const reconstructedOwnerHash = generateOwnerCommitment(ownPublicKey().bytes);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that the user has a vault:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert (vaults.member(userId), "You have no vault position");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load the vault and check the user has enough balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const vault = vaults.lookup(userId);
assert (vault.balance &amp;gt;= amount, "Insufficient vault balance");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then verify that the current public key matches the owner commitment stored when the vault was created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert (reconstructedOwnerHash == vault.ownerHash, "Unauthorized: You are not the owner");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Load the contract-held shielded coin for the vault's coin color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const balanceToSendFrom = balances.lookup(vault.coinColor);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now call &lt;code&gt;sendShielded&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;const sendResult = sendShielded(
    balanceToSendFrom,
    left&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(ownPublicKey()),
    amount
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sendShielded&lt;/code&gt; sends a value from a shielded coin owned by the contract to a recipient.&lt;/p&gt;

&lt;p&gt;Here the recipient is the user creating the transaction, so we use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;left&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(ownPublicKey())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to use ownPublicKey()
&lt;/h3&gt;

&lt;p&gt;&lt;a href="(https://docs.midnight.network/compact/standard-library/exports#ownpublickey)"&gt;&lt;code&gt;ownPublicKey()&lt;/code&gt;&lt;/a&gt; is useful when a circuit needs the current transaction creator's public key as the target of an operation, for example sending a shielded coin to the caller.&lt;/p&gt;

&lt;p&gt;Do not treat &lt;code&gt;ownPublicKey()&lt;/code&gt; as a substitute for a verified private identity by itself. If a circuit authorizes access to private-account state, bind that access to private witness data or to a commitment established earlier in the contract. In this vault, the public key is committed into &lt;code&gt;ownerHash&lt;/code&gt; when the vault is first created, and withdrawal checks that same commitment before sending funds.&lt;/p&gt;

&lt;p&gt;After sending, Compact returns a send result. The important part for this contract is the change. If there is change left, we must store it. If there is no change, we remove the balance entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(sendResult.change.is_some){
    balances.insertCoin(
        vault.coinColor,
        sendResult.change.value,
        right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())
    );
}else{
    balances.remove(vault.coinColor);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reduce the user's vault balance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const updatedVault = Vault {
    ...vault,
    balance: (vault.balance - amount) as Uint&amp;lt;128&amp;gt;
};

vaults.insert(userId, updatedVault);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the compile script again:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you see an error around &lt;code&gt;ownPublicKey&lt;/code&gt;, check the capitalization. It should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ownPublicKey()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Full Contract
&lt;/h2&gt;

&lt;p&gt;Here is the complete &lt;code&gt;src/vault.compact&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;/**
 * Minimal Single Shielded Token Type Vault Dapp
 *
 * Key Concept demonstrated:
 *  Structured onchain pattern for contract state management involving Maps, Struct and hashes
 *  Shielded liquidity managment techniques using using receiveShielded and sendShielded
 *  Risk managment for private state compromise scenario using generateOwnerCommitment and ownerHash field for ownership verification
 *
 * NB: In this contract individual vault balance is publicly visible onchain which is a trade-off to keep the contract minimal as the focus
 * of this example is to demonstrate shielded token management. Usually individual shielded token balance should be kept in private state while commitments are stored onchain
 * utilzing ADT types like Merkle trees (completely shielded and verifiable by zk proof) or Maps 
 */
pragma language_version 0.23.0;
import CompactStandardLibrary;

export struct Vault {
    balance: Uint&amp;lt;128&amp;gt;;
    coinColor: Bytes&amp;lt;32&amp;gt;;
    ownerHash: Bytes&amp;lt;32&amp;gt;;
}

export ledger vaults: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, Vault&amp;gt;;
export ledger balances: Map&amp;lt;Bytes&amp;lt;32&amp;gt;, QualifiedShieldedCoinInfo&amp;gt;;

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

export circuit deposit(
    _coin: ShieldedCoinInfo
): []{
    const coin = disclose(_coin);

    receiveShielded(coin);

    if(balances.member(coin.color)){
        const prevBalance = balances.lookup(coin.color);
        const newBalance = mergeCoinImmediate(prevBalance, coin);

        balances.insertCoin(
            coin.color,
            newBalance,
            right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())
        );
    }else{
        balances.insertCoin(
           coin.color,
           coin,
           right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())
        );
    }

    const userId = generateUserId(disclose(getSecretKey()));
    const hasVault = vaults.member(userId);

    if(!hasVault){
        const ownerCommitmentHash = generateOwnerCommitment(ownPublicKey().bytes);

        const newVault = Vault {
            balance: coin.value,
            coinColor: disclose(coin.color),
            ownerHash: ownerCommitmentHash
        };

        vaults.insert(userId, newVault);
    }else{
        const vault = vaults.lookup(userId);

        assert (coin.color == vault.coinColor, "Invalid coin type deposited");

        const updatedVault = Vault {
            ...vault,
            balance: (vault.balance + coin.value) as Uint&amp;lt;128&amp;gt;
        };

        vaults.insert(userId, updatedVault);
    }
}

export circuit withdraw(_amount: Uint&amp;lt;128&amp;gt;): []{
    const amount = disclose(_amount);
    const userId = generateUserId(disclose(getSecretKey()));

    const reconstructedOwnerHash = generateOwnerCommitment(ownPublicKey().bytes);

    assert (vaults.member(userId), "You have no vault position");

    const vault = vaults.lookup(userId);
    assert (vault.balance &amp;gt;= amount, "Insufficient vault balance");

    assert (reconstructedOwnerHash == vault.ownerHash, "Unauthorized: You are not the owner");

    const balanceToSendFrom = balances.lookup(vault.coinColor);

    const sendResult = sendShielded(
        balanceToSendFrom,
        left&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(ownPublicKey()),
        amount
    );

    if(sendResult.change.is_some){
        balances.insertCoin(
            vault.coinColor,
            sendResult.change.value,
            right&amp;lt;ZswapCoinPublicKey, ContractAddress&amp;gt;(kernel.self())
        );
    }else{
        balances.remove(vault.coinColor);
    }

    const updatedVault = Vault {
        ...vault,
        balance: (vault.balance - amount) as Uint&amp;lt;128&amp;gt;
    };

    vaults.insert(userId, updatedVault);
}

circuit generateUserId(sk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt;{
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "vaul:user"),
        sk
    ]);
}

circuit generateOwnerCommitment(pk: Bytes&amp;lt;32&amp;gt;): Bytes&amp;lt;32&amp;gt;{
    return persistentHash&amp;lt;Vector&amp;lt;2, Bytes&amp;lt;32&amp;gt;&amp;gt;&amp;gt;([
        pad(32, "vault:owner:commitment"),
        pk
    ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 12: Implement the TypeScript Witness
&lt;/h2&gt;

&lt;p&gt;The Compact file declares the witness. TypeScript provides the runtime implementation.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/witness.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;WitnessContext&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="kd"&gt;type&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./managed/contract&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;VaultPrivateState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createVaultPrivateState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;VaultPrivateState&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="nx"&gt;secretKey&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;witnesses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;getSecretKey&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;privateState&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;WitnessContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;VaultPrivateState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;VaultPrivateState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&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;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;privateState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;privateState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretKey&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;The witness returns a tuple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[privateState, witnessValue]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this project, the private state does not change, so the witness returns the same private state it received.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 13: Test the Contract
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/codeBigInt/midnight-simplified-tutorial-dapps/tree/main/vault-dapp" rel="noopener noreferrer"&gt;final project&lt;/a&gt; includes Vitest tests around the generated contract bindings.&lt;/p&gt;

&lt;p&gt;The simulator in &lt;code&gt;src/test/vault-setup.ts&lt;/code&gt; deploys the generated &lt;code&gt;Contract&lt;/code&gt;, initializes private state, and exposes helper methods:&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="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TEST_COIN_COLOR&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Ledger&lt;/span&gt;
&lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Ledger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deploying with empty ledgers&lt;/li&gt;
&lt;li&gt;creating a vault on the first valid deposit&lt;/li&gt;
&lt;li&gt;accumulating two deposits into one vault balance&lt;/li&gt;
&lt;li&gt;rejecting invalid coin colors&lt;/li&gt;
&lt;li&gt;partial withdrawal&lt;/li&gt;
&lt;li&gt;full withdrawal and balance removal&lt;/li&gt;
&lt;li&gt;rejecting withdrawal when the witness secret does not match an existing vault&lt;/li&gt;
&lt;li&gt;rejecting withdrawal when a stolen witness secret is used from a different public key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The accumulation test is important. The core behavior is not only accepting deposits, but merging multiple deposits into one running vault balance:&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;simulator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&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;simulator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vault&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getOnlyVault&lt;/span&gt;&lt;span class="p"&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vault&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="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;bun run &lt;span class="nb"&gt;test&lt;/span&gt;:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test Files  1 passed (1)
Tests       9 passed (9)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What You Learned
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you learned how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;start a Compact project with Bun&lt;/li&gt;
&lt;li&gt;add fast and full compile scripts&lt;/li&gt;
&lt;li&gt;write exported Compact circuits&lt;/li&gt;
&lt;li&gt;define ledger maps&lt;/li&gt;
&lt;li&gt;model a vault position with a struct&lt;/li&gt;
&lt;li&gt;use witness functions for private state&lt;/li&gt;
&lt;li&gt;generate a commitment-based user ID&lt;/li&gt;
&lt;li&gt;receive shielded tokens with &lt;code&gt;receiveShielded&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;store shielded coin balances with &lt;code&gt;insertCoin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;merge new deposits with &lt;code&gt;mergeCoinImmediate&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;send withdrawals with &lt;code&gt;sendShielded&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;manage returned change manually&lt;/li&gt;
&lt;li&gt;test the contract with generated TypeScript bindings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Improvements
&lt;/h2&gt;

&lt;p&gt;This vault is intentionally minimal. If you want to extend it, try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding zero-amount checks&lt;/li&gt;
&lt;li&gt;supporting multiple vaults per user&lt;/li&gt;
&lt;li&gt;extracting repeated balance update logic into helper circuits&lt;/li&gt;
&lt;li&gt;adding events or richer metadata&lt;/li&gt;
&lt;li&gt;building a frontend for deposit and withdraw actions&lt;/li&gt;
&lt;li&gt;supporting only one approved asset instead of multiple coin colors&lt;/li&gt;
&lt;li&gt;moving per-user balances into private state and storing only commitments on-chain&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;This vault dApp is a compact example of how Midnight smart contracts can combine private state, shielded assets, and public ledger updates.&lt;/p&gt;

&lt;p&gt;If you are learning Compact, this is a good project to study because it touches the pieces you need for many real dApps: identity derivation, token transfer, state management, and testing.&lt;/p&gt;

&lt;p&gt;GitHub link: &lt;a href="https://github.com/codeBigInt/midnight-simplified-tutorial-dapps/tree/main/vault-dapp" rel="noopener noreferrer"&gt;https://github.com/codeBigInt/midnight-simplified-tutorial-dapps/tree/main/vault-dapp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>privacy</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>JOURNEY INTO BLOCKCHAIN DEVELOPMENT THE EASY WAY</title>
      <dc:creator>Elliot lucky</dc:creator>
      <pubDate>Sat, 01 Nov 2025 15:28:53 +0000</pubDate>
      <link>https://dev.to/codebigint_01/journey-into-blockchain-development-the-easy-way-40jj</link>
      <guid>https://dev.to/codebigint_01/journey-into-blockchain-development-the-easy-way-40jj</guid>
      <description>&lt;p&gt;Hmmm, thank goodness you found this blog post. I’m sure by now you’ve toured half the internet searching for an easier way to get started in the blockchain industry as a developer. I get it looks complicated, sounds technical, and can feel overwhelming trying to piece everything together.&lt;/p&gt;

&lt;p&gt;That’s exactly why I decided to share my experience to give you a simpler, clearer path into blockchain development and hopefully inspire your own journey.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Like Isaac Newton once said, “If I have seen further, it is by standing on the shoulders of giants.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So before we dive into my personal story, let’s first understand what blockchain really is, and why it was invented in plain, beginner-friendly terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;🔗 WHAT AND WHY BLOCKCHAIN?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s take a scenario.&lt;br&gt;
When you think of a bank, you think about monetary records of people who own accounts i.e., a ledger or journal, although a bit advanced these days with the invention of computers. These ledgers are owned and controlled by these banks. What happens if there’s a fire outbreak and all the financial records become historical ashes (not the cricket trophy, LOL 🤭)? I mean actual ashes.&lt;/p&gt;

&lt;p&gt;Of course, we would not know who owns what. This is one of the reasons blockchain was invented a “distributed/decentralized ledger,” not owned or controlled by a single entity or enterprise.&lt;/p&gt;

&lt;p&gt;These ledger records are distributed in the sense that they are stored on several computers around the globe called “nodes,” each having the same copy of the ledger.&lt;/p&gt;

&lt;p&gt;In today’s world where ledgers are no longer used, it can be likened to a distributed or decentralized database.&lt;/p&gt;

&lt;p&gt;But you may ask, why then is it called a blockchain and not just a decentralized database? Well, instead of storing user transactions or data in rows and columns as banks would do, transactions or data are bundled into small units called “blocks.” And unlike pages of a ledger linked together by glue and referencing each other in the head of the documents, each block is linked together by “hashes”; a fixed-length string that represents the data contained in the block and the next block combined together.&lt;/p&gt;

&lt;p&gt;Enough of the jargon. Back to my experience diving into blockchain development. There are different blockchains, each solving a particular problem e.g., Bitcoin (removes the need for financial intermediaries like banks), Ethereum (eliminating the need for third parties in agreements through smart contracts), Cardano, Midnight (a privacy-centered blockchain), Avalanche, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤯 MY EXPERIENCE DEVELOPING ON ETHEREUM
&lt;/h2&gt;

&lt;p&gt;For me, I started with Ethereum not because it was easy to learn, but because it was one of the most popular at the time and still is. Development on Ethereum introduced the concept of smart contracts, which is now an integral part of every blockchain.&lt;/p&gt;

&lt;p&gt;In fact, smart contracts are what make modern blockchains usable. They are “self-executing” programs or code you write and deploy to the blockchain just like you would on AWS or Vercel with an application you just finished building. They execute themselves when the conditions you define within the contract are met. Like any other application (web or mobile), they have to be coded using a programming language. For Ethereum, I had to learn “Solidity.”&lt;/p&gt;

&lt;p&gt;Solidity was difficult for me to catch up with, as it had a syntax (language writing pattern) that looked like C++ and JavaScript combined. Being a newbie in programming who only had knowledge of web development-centered languages like JavaScript, a bit of TypeScript (a subset of JavaScript), and frontend frameworks like React as at the time, I became overwhelmed.&lt;/p&gt;

&lt;p&gt;I had to learn how to manage memory usage while writing Solidity code, structs, maps, etc. After a while, I knew I was not just ready for it and had to go back to where I came from 😄, web development. Still with the intent of learning blockchain, but with a failed attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡WHAT CHANGED?
&lt;/h2&gt;

&lt;p&gt;Eventually, I came across a sidechain built on the Cardano blockchain called Midnight. Unlike every other blockchain, its language is so easy to learn, and it solves a key pain point in blockchain, decentralization without compromising users’ privacy. On most blockchains, everything is public, including user assets, details, and confidential information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://midnight.network/" rel="noopener noreferrer"&gt;&lt;br&gt;
Midnight blockchain&lt;/a&gt; solves this by ensuring users’ private data is secure and allowing them to choose when to share their info, while still maintaining the decentralized nature expected from every blockchain.&lt;/p&gt;

&lt;p&gt;Most importantly, its smart contract language is really easy to learn. It’s just like TypeScript. That’s to say, if you know TypeScript, you can dive straight in and start developing on Midnight. The language is called Minokawa (formerly Compact). It uses data types similar to arrays, objects, functions, and strings in TypeScript. Except that it differs slightly in how you write them.&lt;/p&gt;

&lt;p&gt;This was when I knew I had found the right spot for me to master and experience what developing on the blockchain feels like without having to learn an entirely strange programming language. Believe me, in less than 3 months I had learned how to write complex smart contracts just because I knew TypeScript. Its ecosystem is enriched with great minds building and collaborating on both individual projects and hackathons (two of which I have won) all in the quest to improve the ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  WHAT I RECOMMEND ⭐⭐⭐⭐⭐
&lt;/h2&gt;

&lt;p&gt;While choosing a blockchain to start developing on depends on the programming languages and concepts one is already familiar with, I would recommend &lt;a href="https://midnight.network/" rel="noopener noreferrer"&gt;Midnight Blockchain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why? Because if you’re someone like me coming from a web or mobile development background, you already know JavaScript. This means you can easily learn TypeScript in less than a month and transition into blockchain development on &lt;a href="https://docs.midnight.network/develop/reference/compact/writing" rel="noopener noreferrer"&gt;Midnight&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Everyone’s blockchain journey is different, but the key is to start where you are and build from what you already know.&lt;/p&gt;

&lt;p&gt;That’s what helped me find my path and hopefully, this helps you find yours too.&lt;/p&gt;

&lt;p&gt;Want to checkout Midnight? Here’s a glimpse of what it feels like writing smart contracts!&lt;br&gt;
&lt;a href="https://docs.midnight.network/develop/reference/compact/writing" rel="noopener noreferrer"&gt;https://docs.midnight.network/develop/reference/compact/writing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stay curious. Keep building. The future is decentralized. ✨&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>midnightchallenge</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
