Build a DApp
Build and deploy decentralized applications (DApps) on the Midnight network. This guide covers toolchain installation, contract compilation, DApp deployment, and node operation. The following example implementation demonstrates a counter contract.
Prerequisites
- Operating System: Linux, macOS, or Windows (via WSL)
- Node.js: v18 LTS or higher (NVM recommended)
- Yarn
- Git
- Terminal: Bash, Zsh, or compatible shell
- Disk Space: ≥ 2 GB
- Network: Internet connection
Objectives
This guide enables developers to:
- Install the tools necessary to compile a Midnight contract and DApp from source code
- Download the example code needed for development
- Build a simple example from source
- Run the example and deploy a smart contract
- Install and run a Midnight network node and its associated Indexer
Use compatible versions of example code and the Compact compiler, as shown in the release compatibility matrix.
The final sections examine the Compact code for the example contract and the TypeScript code for the example DApp in detail.
Upon completion, developers have built a DApp from source, deployed a contract, and run a non-voting Midnight node connected to the Midnight network.
The example contract creates a counter on the ledger and provides a circuit to increment it. The contract enforces only constraints implied by the Counter type. While this example doesn't demonstrate Midnight's full privacy capabilities, it provides the foundation for building and deploying contracts on the Midnight network.
Node
Many Midnight Testnet features are provided as TypeScript packages, including example applications and APIs. These packages require Node.js as their runtime environment and use npm (Node Package Manager) for dependency management. Node Version Manager (NVM) provides the best way to install and manage Node.js versions because it enables switching between different Node versions for different projects and ensures compatibility with Midnight's requirements.
Find installation and troubleshooting instructions on the NVM GitHub site. For macOS users installing via Homebrew, the installation process differs slightly from the standard script installation. Homebrew places NVM in a different directory and requires specific additions to shell profile files for proper initialization.
After following NVM installation instructions, verify installation:
nvm --version
The command displays a version number such as 0.39.5. If the command isn't found, the shell profile modifications weren't applied correctly. Ensure the NVM initialization script is added to the appropriate shell configuration file (~/.bashrc for Bash, ~/.zshrc for Zsh).
Install LTS version of Node 18x or greater:
nvm install 18 --lts
This command downloads and installs the latest Long Term Support version of Node 18. LTS versions receive critical bug fixes and security updates for an extended period, ensuring stability for production applications. The installation includes both Node.js and npm.
Set Node 18 as the default version for new terminal sessions:
nvm alias default 18
Verify the Node installation:
node --version
npm --version
Caution: After modifying
~/.zshrc,~/.bashrc, or installing a new Node version usingnvm, open a new terminal window. Runningsource ~/.zshrcmight not fully reload the environment and could lead to issues such asERR_UNSUPPORTED_DIR_IMPORT. This error occurs when Node.js attempts to import ES modules but the environment variables aren't properly configured.
Install the Compact developer tools
The Compact developer tools manage the installation and updates of the Compact toolchain, including the compiler. These tools solve the complexity of managing multiple compiler versions, platform-specific binaries, and toolchain dependencies. Before these tools existed, developers manually downloaded platform-specific ZIP files, extracted binaries, managed PATH configurations, and repeated this process for each update.
Install the developer tools with a single command:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
This command performs several operations:
- Downloads the installation script using secure HTTPS with TLS 1.2 minimum
- Detects the system architecture (x86_64, ARM64) and operating system
- Downloads the appropriate binary for the platform
- Creates the
~/.compactdirectory structure for toolchain management - Installs the
compactcommand-line tool to~/.cargo/binor another appropriate location
The installation script outputs instructions for adding the binary directory to your PATH environment variable. This step is crucial—without it, the shell cannot find the compact command. The exact instructions depend on your shell and existing PATH configuration. Typically, add a line like this to your shell configuration file:
export PATH="$HOME/.cargo/bin:$PATH"
After adding the directory to your PATH, open a new terminal window or reload your shell configuration. Then update to the latest toolchain:
compact update
This command downloads the latest stable version of the Compact compiler and associated tools. The download includes:
- The Compact compiler binary
- Zero-knowledge proving key generator (
zkir) - Platform-specific runtime dependencies
- Standard library definitions
The output shows the installed version:
compact: aarch64-darwin -- 0.24.0 -- installed
compact: aarch64-darwin -- 0.24.0 -- default.
The first line confirms successful installation. The second line indicates this version is now the default for all compilation operations. The tools maintain multiple versions simultaneously, enabling testing with different compiler versions without conflicts.
Verify the installation
Test the compiler installation to ensure all components are properly configured:
compact compile --version
This command displays the compiler version number, such as 0.24.0. The version check confirms:
- The
compactbinary is accessible via PATH - The default toolchain is properly linked
- The compiler binary has appropriate execution permissions
- All required dependencies are present
If the command fails, common issues include:
- PATH not updated: The shell cannot find the
compactcommand. Verify the installation directory is in your PATH and reload your shell configuration - No default toolchain: Run
compact updateto install and set a default compiler version - Permission issues: On Unix systems, the binaries might lack execution permissions. The installer should handle this automatically, but manual installation might require
chmod +xon the binaries
The version number corresponds to the Compact compiler release, not the developer tools version. These versions are independent—developer tools version 0.1.0 might manage compiler version 0.24.0. Refer to the release compatibility matrix for version compatibility between compiler versions and example code.
Check for updates
Regular update checks ensure access to the latest features, performance improvements, and bug fixes:
compact check
This command performs a network request to determine available updates. The output varies based on your current state:
When updates are available:
compact: aarch64-darwin -- Update Available -- 0.24.0
compact: Latest version available: 0.25.0.
This indicates version 0.25.0 is available for download. The update might include:
- New language features for Compact contracts
- Performance optimizations for proof generation
- Bug fixes for edge cases in compilation
- Enhanced error messages for better debugging
When current:
compact: aarch64-darwin -- Up to date -- 0.24.0
This confirms you're using the latest stable release. Check the Midnight developer announcements for information about upcoming releases and their expected features.
The check command only queries for updates without downloading them. This design enables checking for updates in bandwidth-constrained environments or when you need to coordinate updates across a development team.
Use the Compact compiler
The Compact developer tools provide the standard method to invoke the compiler. Understanding the compilation process helps debug issues and optimize build workflows.
Basic compilation
The standard compilation command:
compact compile <contract file> <output directory>
For example:
compact compile src/counter.compact src/managed/counter
This command triggers several processes:
- Parsing: The compiler reads and validates the Compact contract syntax
- Type checking: Ensures type safety across circuits, witnesses, and ledger operations
- Circuit generation: Converts high-level Compact code into zero-knowledge circuits
- Proving key generation: Creates cryptographic keys for generating and verifying proofs
- TypeScript API generation: Produces type-safe interfaces for DApp integration
The compilation creates multiple output files in the specified directory:
contract/index.d.cts- TypeScript type definitions for the contract APIcontract/index.cjs- JavaScript implementation of the contractzkir/- Directory containing the zero-knowledge circuit representationsproving-keys/- Cryptographic keys for proof generationverifying-keys/- Public keys for proof verification
The compiler reports circuit complexity metrics:
Circuit 'increment' has complexity: 1234 constraints
These metrics indicate the computational cost of generating proofs. Higher constraint counts mean longer proof generation times and higher resource requirements. Optimize circuits to minimize constraints while maintaining security properties.
Version-specific compilation
Override the default compiler version for testing or compatibility:
compact compile +0.23.0 <contract file> <output directory>
This feature enables:
- Testing contracts against different compiler versions
- Maintaining compatibility with deployed contracts compiled with older versions
- Gradual migration when new compiler versions introduce breaking changes
The version specifier (+0.23.0) must reference an already-installed version. Use compact list --installed to see available versions.
Environment variables
The Midnight example DApps historically used environment variables for configuration and toolchain location. Understanding these variables helps when working with existing code or debugging build issues.
Legacy COMPACT_HOME variable: Previous versions of Midnight examples required setting COMPACT_HOME to point to the compiler directory. The new developer tools eliminate this requirement by managing compiler locations internally. The compact command automatically resolves the correct compiler path based on the selected version.
If working with older example code that references COMPACT_HOME, you have two options:
- Update the build scripts: Replace
$COMPACT_HOME/compactcreferences withcompact compilecommands - Set COMPACT_HOME for compatibility: Export the variable pointing to
~/.compact/binfor temporary backward compatibility
Direct compiler access: While not recommended, the installed toolchain binaries reside in ~/.compact/bin/. This directory contains symbolic links to the current default version's binaries:
compactc- The main compiler executablezkir- Zero-knowledge intermediate representation tool- Supporting libraries and runtime files
Direct invocation bypasses version management benefits. Always prefer using compact compile for:
- Automatic version selection
- Consistent behavior across platforms
- Compatibility with future toolchain updates
- Integrated error handling and diagnostics
Project-specific configuration: Modern Midnight projects should document their compiler version requirements in configuration files rather than relying on environment variables. Consider using:
package.jsonscripts that invokecompact compilewith specific versions- Build configuration files that specify the required compiler version
- CI/CD pipelines that install and use specific toolchain versions via
compact update
This approach ensures reproducible builds across different development environments and team members.
Optional: Visual Studio Code extension for Compact
Use any editor to create Midnight DApps. Midnight provides a VSCode extension specifically for creating and editing Midnight contracts written in the Compact DSL. The extension transforms VSCode into a specialized Compact development environment with language-aware features that significantly improve productivity and reduce errors.
Extension features
Syntax highlighting: Color-codes different language elements (keywords, types, functions, comments) for improved readability. The highlighting rules understand Compact-specific constructs like circuit, witness, and ledger declarations.
Live, dynamic contract checking: Performs real-time semantic analysis as you type, identifying errors before compilation. This includes:
- Type checking across circuit boundaries
- Privacy flow analysis to prevent unintended data disclosure
- Witness function signature validation
- Ledger state access verification
Debugging assistance: Provides enhanced error messages with suggested fixes. When compilation fails, the extension highlights problematic code sections and offers context-aware solutions.
Code completion and IntelliSense: Offers intelligent suggestions for:
- Standard library functions and types
- Ledger field access
- Circuit and witness declarations
- Import statements for standard modules
Templates and snippets: Accelerates development with pre-built patterns for:
- New contract scaffolding with standard structure
- Common circuit patterns (authentication, state transitions)
- Witness function declarations
- Standard library imports
Installation process
Download the VSCode extension for Compact from the Midnight Testnet releases repository. The file name follows the pattern compact-x.y.z.vsix for version x.y.z. The VSIX file is a packaged extension format that includes all necessary dependencies.
Install the plugin in VSCode:
- Open the Extensions pane: Click the Extensions icon in the Activity Bar or press
Ctrl+Shift+X(Windows/Linux) orCmd+Shift+X(macOS) - Access installation options: Click the
...symbol at the top of the Extensions pane to reveal additional actions - Select manual installation: Choose "Install from VSIX..." from the dropdown menu
- Locate the downloaded file: Navigate to your Downloads folder or wherever you saved the VSIX file
- Confirm installation: VSCode installs the extension and may prompt for additional permissions
VSCode typically activates newly installed extensions immediately. The extension activates automatically when opening files with the .compact extension. Sometimes VSCode prompts for a restart to ensure all language server components initialize properly.
Configuration
The extension works with default settings but supports customization through VSCode's settings:
- Compiler path: If not using the standard
compactcommand, specify a custom compiler location - Validation level: Adjust the strictness of real-time checking (errors only, include warnings, include suggestions)
- Format on save: Enable automatic code formatting when saving files
Access extension settings through File > Preferences > Settings and search for "Compact" to find all available options.
Even if VSCode isn't your primary editor, consider using the VSCode Compact extension for editing Midnight contracts while learning the language. The immediate feedback and intelligent assistance accelerate the learning process and help avoid common mistakes.
Manage toolchain versions
The Compact developer tools support multiple toolchain versions simultaneously. This capability is essential for maintaining existing contracts while developing new ones, testing compatibility across versions, and gradually migrating to newer compiler releases.
List available versions
View all versions available for download:
compact list
Output shows versions and supported platforms:
compact: available versions
→ 0.24.0 - x86_macos, aarch64_macos, x86_linux
0.23.0 - aarch64_macos, x86_linux
0.22.0 - x86_macos, x86_linux
The arrow (→) indicates the current default version used when running compact compile without a version override. Platform indicators show which architectures support each version:
x86_macos- Intel-based Mac computersaarch64_macos- Apple Silicon Macs (M1, M2, M3)x86_linux- Standard Linux on Intel/AMD processors
Not all versions support all platforms. Early releases might lack Apple Silicon support, while some versions might skip certain platforms due to build issues.
Check installed versions
List versions downloaded to your system:
compact list --installed
Output shows only locally available versions:
compact: installed versions
→ 0.24.0
0.23.0
Installed versions consume disk space (approximately 100-200MB each) but enable offline compilation and instant version switching. Remove unused versions by deleting their directories from ~/.compact/versions/.
Switch between versions
Change the default toolchain version:
compact update 0.23.0
This command:
- Downloads the specified version if not already installed
- Verifies the download integrity using checksums
- Updates the symbolic link at
~/.compact/binto point to the new version - Confirms the switch with output showing the new default
The switch affects all subsequent compact compile commands unless overridden with the +version syntax. Projects can document their required compiler version in README files or build scripts to ensure consistency across team members.
Developer tools maintenance
The developer tools update themselves independently from the toolchain. This separation ensures that improvements to the version management system don't require compiler updates, and compiler releases don't force tool updates. The architecture enables the tools to manage multiple compiler versions while maintaining a consistent interface.
Check for tool updates
Verify if newer developer tools are available:
compact self check
This command queries the GitHub releases API to identify the latest stable version. The check compares your installed version against the latest release and reports:
- Current installed version of the developer tools
- Latest available version
- Whether an update is recommended
The tools check for updates automatically once per day when running any compact command. This passive check doesn't interrupt workflow but notifies about available updates through a brief message after command completion.
Update the developer tools
Install the latest developer tools version:
compact self update
The self-update process:
- Downloads the latest version to a temporary location
- Verifies the download using cryptographic signatures
- Replaces the current binary with the new version
- Preserves all installed toolchains and configuration
Self-updates are backward compatible—new tool versions continue to work with existing installed toolchains. This design principle ensures that updating tools never breaks existing projects.
When to update
Update the developer tools when:
- New features become available: Future releases include features like
compact formatfor code formatting,compact docfor documentation generation, andcompact testfor contract testing - Bug fixes are released: Tool updates may resolve issues with version management, platform compatibility, or command execution
- Security updates: Although rare, security updates to the tools themselves should be applied promptly
The release notes for each tool version (distinct from compiler release notes) describe new features and important changes. Monitor the Midnight developer announcements for significant tool updates that enhance the development experience.
Get help
The compact tool provides comprehensive built-in documentation accessible directly from the command line. This integrated help system eliminates the need to search online documentation for basic command syntax and options.
General help
Access the main help overview:
compact help
compact --help
Both commands display identical output—a complete list of available subcommands with brief descriptions. The help text includes:
- Subcommand list: All available operations like
compile,update,list,check - Global options: Flags that apply to all subcommands, such as
--directoryfor specifying the toolchain location - Usage examples: Basic command patterns showing typical invocations
The help system uses a hierarchical structure. The top-level help provides an overview, while subcommand-specific help offers detailed information about individual operations.
Subcommand help
Get detailed help for specific operations:
compact help update
compact update --help
Subcommand help includes:
- Detailed description: Explains what the command does and when to use it
- Argument specifications: Required and optional parameters with their types
- Flag descriptions: All available options with their effects
- Examples: Real-world usage scenarios
- Related commands: References to similar or complementary operations
For compiler-specific help:
compact compile --help
This displays compiler options including:
- Input specifications: Supported file formats and contract structures
- Output options: Directory structure and generated file descriptions
- Optimization flags: Options for controlling compilation behavior
- Debug options: Flags for generating additional diagnostic information
Version information
The tools provide multiple version queries for different components:
Developer tools version:
compact --version
Returns the version of the compact command itself (e.g., 0.1.0). This version indicates the capabilities of the version management system.
Compiler/toolchain version:
compact compile --version
Returns the version of the currently selected Compact compiler (e.g., 0.24.0). This version determines available language features and compilation behavior.
Compact language version:
compact compile --language-version
Returns the version of the Compact language specification supported by the current compiler. Language versions change less frequently than compiler versions, as they represent the stable language syntax and semantics.
Understanding version relationships helps diagnose compatibility issues:
- Language version: Defines contract syntax and available features
- Compiler version: Implements a specific language version with particular optimizations
- Developer tools version: Manages compiler installations and provides development utilities
When reporting issues or seeking support, provide all three version numbers for complete context.