Skip to main content

Create a Midnight Network app

A Midnight Network (MN) app is a privacy-preserving DApp that uses zero-knowledge proofs (ZKPs) to maintain data confidentiality. The core value proposition is selective disclosure, which allows users to prove specific information while keeping other sensitive data private.

Quick start with create-mn-app

The create-mn-app CLI tool scaffolds Midnight Network applications with zero configuration. The tool provides a preconfigured TypeScript setup, hot reloading, and wallet generation, with automatic dependency management for Node.js, Docker, and the Compact compiler. To install, run the following commands:

npx create-mn-app my-app
cd my-app
npm run setup

Available templates include Hello World (message storage) and Counter DApp (increment/decrement with zkProofs). Additional templates for Bulletin Board, DEX, and Midnight Kitties are in development.

For manual setup, follow these steps:

Prerequisites

The following software and tools are required:

  • Node.js: Version 20.x or higher. Install Node.js using NVM.
    nvm install 20
  • Command-line knowledge: Basic familiarity with terminal operations.
  • Code editor: An IDE such as Visual Studio Code.

Create a project

1

Initialize the project. Create a project folder, navigate to the folder, and initialize an npm project. The -y flag accepts all default settings.

mkdir my-mn-app
cd my-mn-app
npm init -y
2

Create the required directories. The project requires separate folders for smart contracts and application source code.

mkdir src contracts

The project structure now appears as follows:

my-mn-app/
├── contracts/
├── src/
└── package.json
3

Create the contract file. Create a file named hello-world.compact in the contracts directory.

touch contracts/hello-world.compact

Add the pragma language version directive

The pragma language_version directive specifies the Compact language version with which the contract is compatible. While optional, using this directive is recommended to protect against breaking changes when new language versions are released.

Until Compact 1.0, minor version updates may include breaking changes. Specifying the exact language version ensures that semantic changes in future versions do not inadvertently affect your contract's behavior.

4

Add the pragma directive. Add the following line to the top of contracts/hello-world.compact. This directive specifies language version 0.17.

pragma language_version 0.17;

Define the ledger declaration

The ledger section defines the on-chain state variables for a contract. This section functions as a schema for data stored permanently on the blockchain. Variables can be marked with export to make them accessible from a DApp.

Syntax:

ledger <identifier>: <type>;
export ledger <identifier>: <type>;

This contract uses the Opaque<"string"> type, which stores variable-length strings while preserving privacy.

5

Create the ledger declaration. This line reserves on-chain storage for a string variable named message.

pragma language_version 0.17;

export ledger message: Opaque<"string">;

Define the circuit

Circuits are the functions of a Compact smart contract and are compiled directly into a zero-knowledge circuit. Circuits can accept parameters, perform computations, and modify the contract state. Circuits marked with export can be called from external applications.

Syntax:

circuit <name>(<parameters>): <return_type> { <body> }
export circuit <name>(<parameters>): <return_type> { <body> }
6

Define the storeMessage circuit. This circuit accepts a custom message as input and stores the message in the message state variable.

export circuit storeMessage(customMessage: Opaque<"string">): [] {
message = disclose(customMessage);
}
Why disclose is required

Compact enforces privacy by default. All user input is considered private unless explicitly made public. The disclose operator indicates that the message is intentionally visible on the public ledger.

The complete hello-world.compact contract appears as follows:

pragma language_version 0.17;

export ledger message: Opaque<"string">;

export circuit storeMessage(customMessage: Opaque<"string">): [] {
message = disclose(customMessage);
}

Compile the smart contract

Compiling a Compact contract transforms the high-level logic into zero-knowledge circuits, generates cryptographic keys, and creates TypeScript APIs for the DApp frontend. This process enforces Midnight's privacy and security guarantees.

7

Compile the contract. Run the following command from the project root directory.

compact compile contracts/hello-world.compact contracts/managed/hello-world

This command generates the necessary artifacts in the contracts/managed/hello-world directory. The generated structure appears as follows:

contracts/
└── managed
└── hello-world
├── compiler
├── contract
├── keys
└── zkir
  • contract/: Contains the compiled contract artifacts, including JSON files required for deployment and frontend integration.
  • keys/: Stores the cryptographic proving and verifying keys for the zero-knowledge proofs.
  • zkir/: Contains the Zero-Knowledge Intermediate Representation of the circuit, which bridges the Compact code to the ZK backend.
  • compiler/: Holds intermediate files used by the compiler during the build process.