iToverDose/Software· 11 MAY 2026 · 00:04

Build a private token dApp with zero-knowledge proofs on Midnight

Learn how to deploy a shielded token smart contract on Midnight, verify transactions with ZK-proofs, and connect a React frontend without exposing balances on-chain.

DEV Community3 min read0 Comments

Midnight’s shielded token design lets developers create decentralized apps where every token transfer happens in private. Unlike transparent blockchains, amounts and recipient addresses stay hidden from public explorers, while the contract still enforces correctness through zero-knowledge proofs.

A new tutorial shows how to go from a private Compact smart contract to a fully working React dApp that users can run in their browsers. The guide covers deploying the contract, generating proofs for minting and transfers, and building a wallet-ready UI that never reveals sensitive data on-chain.

Why shielded tokens matter

Shielded tokens keep financial data confidential by design. On Midnight, only the wallet owner can decrypt their balance, while the network only sees aggregate metrics like total supply. This preserves privacy without sacrificing verifiability—all transactions are proven correct, just not revealed.

The tutorial’s smart contract uses Midnight’s Compact language to define public counters and private operations. While totalSupply and totalBurned remain visible, individual transfers and balances stay encrypted. Zero-knowledge proofs validate every mint, transfer, and burn without exposing the underlying values.

Setting up the development environment

Before writing code, developers need the right tools. The tutorial assumes Node.js 20 or later, a Midnight-compatible wallet such as 1AM or Lace, and some Preprod NIGHT tokens from the official faucet. These tokens cover gas fees during testing and deployment.

A starter package.json includes all required Midnight SDK packages: runtime libraries, proof providers, and React integration tools. Key dependencies include the Compact runtime, dApp connector API, ledger utilities, and front-end libraries like React Router and Zustand for state management.

{
  "dependencies": {
    "@midnight-ntwrk/compact-runtime": "latest",
    "@midnight-ntwrk/dapp-connector-api": "latest",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.22.0",
    "zustand": "^4.5.0"
  }
}

Writing the Compact smart contract

The contract, located in contracts/Token.compact, starts with two public counters: totalSupply and totalBurned. These track the token lifecycle without exposing individual balances.

ledger totalSupply: Uint<64>;
ledger totalBurned: Uint<128>;

Each shielded coin needs a unique nonce to prevent replay attacks. The contract defines a local witness that generates a fresh 32-byte random value for every mint operation.

witness localNonce(): Bytes<32>;

The createShieldedToken circuit mints a new private token by combining the nonce, amount, and recipient. The disclose() calls expose only what the ledger needs to route the output—never the actual token value.

export circuit createShieldedToken(
  amount: Uint<64>,
  recipient: Either<ZswapCoinPublicKey, ContractAddress>
): ShieldedCoinInfo {
  const domain = pad(32, "shielded:token");
  const nonce = localNonce();
  const coin = mintShieldedToken(
    disclose(domain),
    disclose(amount),
    disclose(nonce),
    disclose(recipient)
  );
  totalSupply = (totalSupply + disclose(amount)) as Uint<64>;
  return coin;
}

Atomic mint-and-send in one transaction

Most shielded transactions require a Merkle proof to spend an existing coin. Midnight’s mintAndSend circuit bypasses this by minting and spending in the same atomic operation. The contract creates a coin and forwards it to the recipient without waiting for Merkle inclusion.

export circuit mintAndSend(
  amount: Uint<64>,
  recipient: Either<ZswapCoinPublicKey, ContractAddress>
): ShieldedSendResult {
  const domain = pad(32, "shielded:token");
  const nonce = localNonce();
  const coin = mintShieldedToken(
    disclose(domain),
    disclose(amount),
    disclose(nonce),
    right<ZswapCoinPublicKey, ContractAddress>(kernel.self())
  );
  const result = sendImmediateShielded(
    disclose(coin),
    disclose(recipient),
    disclose(amount) as Uint<128>
  );
  totalSupply = (totalSupply + disclose(amount)) as Uint<64>;
  return result;
}

The result includes both the sent coin and any change if the amount exceeds the recipient’s balance. This pattern is ideal for self-custody wallets that receive private tokens directly.

Building the React frontend

With the contract deployed, the next step is a browser-based UI. The tutorial uses React Router for navigation and Zustand for wallet state. A key challenge is handling zero-knowledge proofs in the client: users must generate proofs locally before submitting transactions.

The wallet SDK connects to Midnight’s network, fetches the latest Zswap root, and prepares proofs for transfers. The UI renders balance snapshots decrypted client-side, ensuring no sensitive data ever touches the public ledger.

What’s next for shielded dApps

Midnight’s approach to private tokens opens doors for compliant DeFi, confidential DAOs, and enterprise-grade asset issuance. As the network matures, expect better tooling for proof generation, lighter clients for mobile devices, and tighter integration with existing wallets. Developers who start now will shape the next generation of privacy-first applications.

AI summary

Midnight blok zincirinde gizli token uygulaması oluşturmanın püf noktalarını keşfedin. Compact sözleşme, ZK-proofs ve React arayüzüyle adım adım rehber.

Comments

00
LEAVE A COMMENT
ID #OUBGTV

0 / 1200 CHARACTERS

Human check

8 + 3 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.