An open API service indexing awesome lists of open source software.

https://github.com/midnightntwrk/midnight-local-dev

Midnight local development environment
https://github.com/midnightntwrk/midnight-local-dev

compact midnightntwrk

Last synced: about 1 month ago
JSON representation

Midnight local development environment

Awesome Lists containing this project

README

          

# Midnight Local Network

A standalone tool for running a local Midnight development network and funding test accounts. It handles Docker container orchestration, genesis wallet initialization, NIGHT token transfers, and DUST registration — so your DApp projects can connect to a ready-to-use local blockchain without managing infrastructure themselves.

## Table of Contents

- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Network Services](#network-services)
- [Funding Options](#funding-options)
- [Option 1: Fund from Config File (NIGHT + DUST)](#option-1-fund-from-config-file-night--dust)
- [Option 2: Fund by Public Key (NIGHT Only)](#option-2-fund-by-public-key-night-only)
- [Connecting Your DApp](#connecting-your-dapp)
- [Running the Network Standalone (Without the Funding CLI)](#running-the-network-standalone-without-the-funding-cli)
- [Accounts Config File Format](#accounts-config-file-format)
- [Environment Variables](#environment-variables)
- [Architecture](#architecture)
- [Troubleshooting](#troubleshooting)

---

## Prerequisites

- **Node.js** >= 22.0.0
- **Docker** and **Docker Compose** (v2)
- Access to the Midnight npm registry (for `@midnight-ntwrk/*` packages)

## Installation

```bash
cd midnight-local-dev
npm install
```

## Quick Start

```bash
npm start
```

This single command will:

1. **Detect** if a local Midnight network is already running
- If running: prompt to **reuse** it or **restart** with fresh images
- If not running: **pull** Docker images and **start** all containers
2. **Initialize** the genesis master wallet (seed `0x00...001`) which holds all minted NIGHT tokens
3. **Register DUST** for the master wallet (required to pay transaction fees)
4. **Display** the master wallet balance
5. **Present an interactive menu** for funding test accounts

If a network is already running, you'll first see:

```
A local dev network is already running:
[1] Use the existing network
[2] Stop containers, pull latest images, and restart
>
```

Then the main menu:

```
Choose an option:
[1] Fund accounts from config file (NIGHT + DUST registration)
[2] Fund accounts by public key (NIGHT transfer only)
[3] Display master wallet balances
[4] Exit
>
```

When you select `[4] Exit` or press `Ctrl+C`, the tool gracefully shuts down all wallets. Docker containers are only stopped if they were started by this session (reusing an existing network leaves containers running).

---

## Network Services

The local network runs three Docker containers on fixed ports:

| Service | Container Name | Host Port | URL |
|---|---|---|---|
| **Midnight Node** | `midnight-node` | `9944` | `http://localhost:9944` |
| **Indexer** (GraphQL) | `midnight-indexer` | `8088` | `http://localhost:8088/api/v3/graphql` |
| **Indexer** (WebSocket) | `midnight-indexer` | `8088` | `ws://localhost:8088/api/v3/graphql/ws` |
| **Proof Server** | `midnight-proof-server` | `6300` | `http://localhost:6300` |

These ports match the defaults hardcoded by the **Lace wallet extension** when configured for the `undeployed` network type. This means Lace connects to the local network out of the box — no custom endpoint configuration required. Just select "Undeployed" in Lace's network settings and it will point to `localhost:9944`, `localhost:8088`, and `localhost:6300` automatically.

All services use the `undeployed` network ID with the `dev` node preset.

### Docker Images

| Service | Image | Version |
|---|---|---|
| Node | `midnightntwrk/midnight-node` | `0.22.3` |
| Indexer | `midnightntwrk/indexer-standalone` | `4.0.1` |
| Proof Server | `midnightntwrk/proof-server` | `8.0.3` |

### Wallet SDK Compatibility Matrix

| Package | Version |
|---|---|
| `@midnight-ntwrk/wallet-sdk-facade` | 3.0.0 |
| `@midnight-ntwrk/wallet-sdk-abstractions` | 2.0.0 |
| `@midnight-ntwrk/wallet-sdk-shielded` | 2.1.0 |
| `@midnight-ntwrk/wallet-sdk-dust-wallet` | 3.0.0 |
| `@midnight-ntwrk/wallet-sdk-unshielded-wallet` | 2.1.0 |
| `@midnight-ntwrk/wallet-sdk-address-format` | 3.1.0 |
| `@midnight-ntwrk/wallet-sdk-hd` | 3.0.1 |
| `@midnight-ntwrk/ledger-v8` | 8.0.3 |
| `@midnight-ntwrk/midnight-js-network-id` | 4.0.2 |

---

## Funding Options

Each funding operation transfers **50,000 NIGHT** (in smallest denomination: `50,000 * 10^6`) from the genesis master wallet. You can fund up to **10 accounts** per operation.

### Option 1: Fund from Config File (NIGHT + DUST)

Best for development setups where you control the test wallets. Provide a JSON file with BIP39 mnemonics. For each account, the tool will:

1. Derive a wallet from the mnemonic
2. Transfer 50,000 NIGHT from the master wallet
3. Wait for the recipient to see the funds
4. Register the recipient's NIGHT UTXOs for DUST generation
5. Wait for DUST to be available

After this, each account is **fully ready** to submit transactions (deploy contracts, call circuits, etc.) because they have both NIGHT (for value) and DUST (for fees).

```
> 1
Path to accounts JSON file: ./accounts.json
```

See [Accounts Config File Format](#accounts-config-file-format) for the JSON schema.

### Option 2: Fund by Public Key (NIGHT Only)

Best when you already have wallet addresses (e.g., from the Lace wallet or another DApp) and just need tokens. Provide one or more Bech32 addresses separated by commas.

```
> 2
Enter Bech32 addresses (comma-separated): mn1q..., mn1q...
```

Each address receives 50,000 NIGHT. **DUST is not registered** — recipients must register for DUST themselves before they can pay transaction fees.

---

## Connecting Your DApp

Once the network is running, any Midnight DApp can connect using the standard localhost endpoints. No special configuration needed.

### Example: ZKLoan Credit Scorer CLI

In a **separate terminal**, while the network is running:

```bash
cd ../zkloan-credit-scorer/zkloan-credit-scorer-cli
npm run standalone
```

The CLI's standalone mode connects to the same fixed ports (`9944`, `8088`, `6300`) without starting its own Docker containers.

### Example: Custom TypeScript DApp

```typescript
import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

setNetworkId('undeployed');

const config = {
indexer: 'http://127.0.0.1:8088/api/v3/graphql',
indexerWS: 'ws://127.0.0.1:8088/api/v3/graphql/ws',
node: 'http://127.0.0.1:9944',
proofServer: 'http://127.0.0.1:6300',
networkId: 'undeployed',
};

// Use these endpoints with the Midnight wallet SDK, contract deployment, etc.
```

### Example: UI Development (React / Vite)

Point your UI's environment variables to the local endpoints:

```env
VITE_INDEXER_URL=http://localhost:8088/api/v3/graphql
VITE_INDEXER_WS_URL=ws://localhost:8088/api/v3/graphql/ws
VITE_NODE_URL=http://localhost:9944
VITE_PROOF_SERVER_URL=http://localhost:6300
VITE_NETWORK_ID=undeployed
```

---

## Running the Network Standalone (Without the Funding CLI)

If you only need the Docker containers running (no wallet initialization or funding), you can use Docker Compose directly:

```bash
# Pull images and start all services
docker compose -f standalone.yml up -d

# Check service health
docker compose -f standalone.yml ps

# View logs
docker compose -f standalone.yml logs -f

# Stop everything
docker compose -f standalone.yml down
```

This is useful when:
- Your DApp handles its own wallet initialization
- You want to keep the network running across multiple test sessions
- You're debugging container-level issues

Note: In this mode you must handle genesis wallet funding and DUST registration yourself.

---

## Accounts Config File Format

Create a JSON file with the following structure:

```json
{
"accounts": [
{
"name": "Alice",
"mnemonic": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
},
{
"name": "Bob",
"mnemonic": "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote"
}
]
}
```

| Field | Type | Description |
|---|---|---|
| `accounts` | `array` | List of accounts to fund (max 10) |
| `accounts[].name` | `string` | Display name for logging |
| `accounts[].mnemonic` | `string` | BIP39 mnemonic phrase (24 words) |

An example file is provided at `accounts.example.json`.

**Security note:** These mnemonics are for **local development only**. Never use real wallet mnemonics in config files.

---

## Environment Variables

Copy `.env.example` to `.env` and configure as needed:

| Variable | Default | Description |
|---|---|---|
| `DEBUG_LEVEL` | `info` | Log level: `trace`, `debug`, `info`, `warn`, `error`, `fatal` |

---

## Architecture

```
┌─────────────────────────────────────────────────┐
│ midnight-local-network │
│ │
│ src/index.ts Entry point & interactive menu │
│ src/network.ts Docker compose orchestration │
│ src/wallet.ts Wallet SDK operations │
│ src/funding.ts NIGHT transfer & DUST reg. │
│ src/config.ts Network endpoint config │
│ src/logger.ts Pino logger setup │
└──────────┬──────────────────────────┬────────────┘
│ │
testcontainers Wallet SDK
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────────┐
│ Docker Compose │ │ Midnight Blockchain │
│ │ │ │
│ ┌────────────┐ │ │ Genesis Wallet (master) │
│ │ Node │◄─┼───┤ ├─ Transfer NIGHT │
│ │ :9944 │ │ │ ├─ Register DUST │
│ └────────────┘ │ │ └─ Fund recipients │
│ ┌────────────┐ │ │ │
│ │ Indexer │ │ │ Recipient Wallets │
│ │ :8088 │ │ │ ├─ From mnemonic │
│ └────────────┘ │ │ └─ From Bech32 address │
│ ┌────────────┐ │ └──────────────────────────┘
│ │ Proof │ │
│ │ Server │ │
│ │ :6300 │ │
│ └────────────┘ │
└──────────────────┘
```

### Startup Sequence

```
1. Network detection
├── Check if midnight-node, midnight-indexer, midnight-proof-server are running
├── If running → prompt: reuse existing or restart fresh
└── If not running → docker compose up
├── midnight-node starts (waits for health: /health endpoint)
├── midnight-indexer starts (waits for: "starting indexing" log)
└── midnight-proof-server starts (waits for: "Actix runtime found" log)

2. Master wallet initialization (WalletFacade.init)
├── Derive HD wallet from genesis seed (0x00...001)
├── Create unified DefaultConfiguration
├── Initialize WalletFacade with shielded, unshielded, and dust builders
├── Start wallet facade and wait for sync
└── Display genesis NIGHT balance

3. DUST registration for master wallet
├── Find unregistered Night UTXOs
├── Submit registration transaction
└── Wait for dust balance > 0

4. Interactive menu (fund accounts, check balances, exit)

5. Cleanup on exit
├── Close all wallet connections
└── docker compose down (only if network was started by this session)
```

### Key Concepts

- **NIGHT**: The native token on Midnight. The genesis block mints a large supply accessible via the master wallet seed.
- **DUST**: Transaction fees on Midnight are paid in DUST, which is generated by registering NIGHT UTXOs. Without DUST registration, a wallet cannot submit transactions even if it holds NIGHT.
- **Master Wallet**: The genesis wallet (seed `0x00...001`) that holds all initially minted tokens. All funding transfers originate from this wallet.
- **Unshielded vs Shielded**: NIGHT can be held in unshielded (public) or shielded (private) form. This tool transfers unshielded NIGHT.

---

## Troubleshooting

### Port already in use

```
Error: Bind for 0.0.0.0:9944 failed: port is already allocated
```

Another process or a previous run is using the port. Stop it:

```bash
docker compose -f standalone.yml down
# or find and kill the process
lsof -i :9944
```

### Containers not starting

Check Docker is running and you have access to the Midnight Docker registry:

```bash
docker compose -f standalone.yml pull
docker compose -f standalone.yml up
```

Watch the logs for specific errors:

```bash
docker compose -f standalone.yml logs -f node
docker compose -f standalone.yml logs -f indexer
docker compose -f standalone.yml logs -f proof-server
```

### Wallet sync takes too long

The indexer needs time to catch up with the node after startup. If sync seems stuck:

1. Check the indexer logs: `docker compose -f standalone.yml logs -f indexer`
2. Verify the node is producing blocks: `curl http://localhost:9944/health`
3. Set `DEBUG_LEVEL=debug` in `.env` for more detailed wallet logs

### DUST registration fails

DUST registration requires the wallet to have synced and found its NIGHT UTXOs. If it fails:

1. Ensure the wallet has a non-zero NIGHT balance
2. Check the proof server is healthy: `curl http://localhost:6300/version`
3. Try again — transient network issues during startup can cause one-time failures

### Logs

Logs are written to both the console and a timestamped file in the `logs/` directory:

```bash
ls logs/
# 2026-02-24T12:00:00.000Z.log
```