TypeScript application development framework for the Midnight blockchain. Similar to Web3.js for Ethereum or polkadot.js for Polkadot.
yarn add @midnight-ntwrk/midnight-jsThe barrel package covers contracts, types, utilities and the protocol re-exports, through the subpaths @midnight-ntwrk/midnight-js/contracts, /types, /utils and /protocol. Providers are separate packages. Add the ones you use, for example those in the Quick Start below:
yarn add @midnight-ntwrk/midnight-js-level-private-state-provider \
@midnight-ntwrk/midnight-js-indexer-public-data-provider \
@midnight-ntwrk/midnight-js-http-client-proof-provider \
@midnight-ntwrk/midnight-js-fetch-zk-config-providerimport { deployContract } from '@midnight-ntwrk/midnight-js/contracts';
import { createLedger9MidnightProvider, createLedger9WalletProvider } from '@midnight-ntwrk/midnight-js/types';
import { createMidnightProviders } from '@midnight-ntwrk/midnight-js/utils';
import { levelPrivateStateProvider } from '@midnight-ntwrk/midnight-js-level-private-state-provider';
import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';
import { httpClientProofProvider } from '@midnight-ntwrk/midnight-js-http-client-proof-provider';
import { FetchZkConfigProvider } from '@midnight-ntwrk/midnight-js-fetch-zk-config-provider';
const password = process.env.PRIVATE_STATE_PASSWORD;
if (!password) {
throw new Error('Set PRIVATE_STATE_PASSWORD');
}
const zkConfigProvider = new FetchZkConfigProvider('https://artifacts.example.com');
const providers = createMidnightProviders({
privateStateProvider: levelPrivateStateProvider({
privateStoragePasswordProvider: () => password,
accountId: myWallet.getCoinPublicKey()
}),
publicDataProvider: indexerPublicDataProvider({
queryURL: 'https://indexer.example.com/graphql',
subscriptionURL: 'wss://indexer.example.com/graphql'
}),
zkConfigProvider,
proofProvider: httpClientProofProvider({ url: 'https://proof-server.example.com', zkConfigProvider }),
walletProvider: createLedger9WalletProvider(myWallet),
midnightProvider: createLedger9MidnightProvider((tx) => myWallet.submitTx(tx)),
config: { networkId: 'preview', ttlSeconds: 3600 }
});
// Deploy a contract
const deployed = await deployContract(providers, {
compiledContract: myContract,
privateStateId: 'my-state',
initialPrivateState: { counter: 0n }
});
// Call a circuit
const result = await deployed.callTx.increment();What the snippet expects from you:
myWalletis your wallet integration. It implementsbalanceTx,getCoinPublicKeyandgetEncryptionPublicKey, and submits finalized transactions. For one built on@midnightntwrk/wallet-sdk, seeMidnightWalletProviderin testkit-js. In a browser with a wallet extension, see In a browser.myContractis your compiled Compact contract. See Contracts.PRIVATE_STATE_PASSWORDencrypts private state at rest. It needs at least 16 characters, at least 3 of uppercase, lowercase, digits and special characters, no more than 3 identical characters in a row, and no sequences such as1234orabcd.createMidnightProviderschecks at start-up that every required provider and theconfigare present.
In a browser dApp, the user's wallet extension (for example Lace) proves, balances and submits. Build those three providers from the wallet's ConnectedAPI with @midnight-ntwrk/midnight-js-dapp-connector-proof-provider. Add the packages this needs:
yarn add @midnight-ntwrk/midnight-js-dapp-connector-proof-provider @midnight-ntwrk/midnight-js-protocolimport {
dappConnectorProofProvider,
dappConnectorWalletProviders
} from '@midnight-ntwrk/midnight-js-dapp-connector-proof-provider';
import { CostModel } from '@midnight-ntwrk/midnight-js-protocol/v9';
const connectedAPI = await initialAPI.connect('preview');
const proofProvider = await dappConnectorProofProvider(connectedAPI, zkConfigProvider, CostModel.initialCostModel());
const { walletProvider, midnightProvider } = await dappConnectorWalletProviders(connectedAPI);initialAPI is the wallet's entry in window.midnight. Pass the three providers to createMidnightProviders in place of httpClientProofProvider, createLedger9WalletProvider and createLedger9MidnightProvider above. All three serve ledger 8 and ledger 9 transactions. The connector takes no TTL, so the wallet picks the expiry and config.ttlSeconds has no effect on balancing (midnight-dapp-connector-api#102).
httpClientProofProvider proves transactions for both the current ledger era and the retained one (the era before the last hard fork). createLedger9WalletProvider and createLedger9MidnightProvider serve the current era only. To handle retained-era transactions too, build the wallet and midnight providers with createWalletProviderFromHandlers and createMidnightProviderFromHandlers. See ADR 0014.
Complete dApps live in separate repositories:
- create-mn-app: scaffolds a new Midnight project
- midnight-examples
- example-hello-world, example-bboard and example-battleship
These repositories may track the latest stable release of Midnight.js rather than main, so their API calls can differ from the Quick Start above.
- Creating and submitting transactions
- Interacting with wallets
- Querying block and contract state
- Subscribing to chain events
- Executing smart contracts locally
- Incorporating private state into contract execution
- Persisting, querying, and updating private state
- Creating and verifying zero-knowledge proofs
| Package | Description |
|---|---|
| @midnight-ntwrk/midnight-js | Barrel package for Midnight.js core framework |
| @midnight-ntwrk/midnight-js-types | Shared types, interfaces, and provider contracts |
| @midnight-ntwrk/midnight-js-contracts | Contract deployment and interaction utilities |
| @midnight-ntwrk/midnight-js-dapp-connector-proof-provider | Proof, wallet and midnight providers delegating to a DApp Connector wallet |
| @midnight-ntwrk/midnight-js-indexer-public-data-provider | GraphQL-based blockchain data provider |
| @midnight-ntwrk/midnight-js-level-private-state-provider | Encrypted LevelDB private state storage |
| @midnight-ntwrk/midnight-js-http-client-proof-provider | HTTP client for proof server |
| @midnight-ntwrk/midnight-js-fetch-zk-config-provider | Browser-based ZK artifact retrieval |
| @midnight-ntwrk/midnight-js-node-zk-config-provider | Node.js filesystem-based ZK artifact retrieval |
| @midnight-ntwrk/midnight-js-logger-provider | Pino logger wrapper for diagnostics |
| @midnight-ntwrk/midnight-js-compact | Compact compiler manager |
| @midnight-ntwrk/midnight-js-protocol | Era-named re-exports of Midnight protocol packages (/v8, /v9, /v9/*) |
| @midnight-ntwrk/midnight-js-utils | General utility functions |
| Element | Package |
|---|---|
| Contracts | @midnight-ntwrk/midnight-js-contracts |
| PublicDataProvider | @midnight-ntwrk/midnight-js-indexer-public-data-provider |
| PrivateStateProvider | @midnight-ntwrk/midnight-js-level-private-state-provider |
| ProofProvider | @midnight-ntwrk/midnight-js-http-client-proof-provider or @midnight-ntwrk/midnight-js-dapp-connector-proof-provider |
| WalletProvider, MidnightProvider | your wallet integration, or @midnight-ntwrk/midnight-js-dapp-connector-proof-provider |
| ZKConfigProvider | @midnight-ntwrk/midnight-js-fetch-zk-config-provider |
| DappConnector | @midnightntwrk/dapp-connector-api |
| Wallet | @midnightntwrk/wallet-sdk |
| Ledger | @midnightntwrk/ledger-v9 (current era), @midnightntwrk/ledger-v8 (retained era) |
| Compact Runtime | @midnight-ntwrk/compact-runtime |
| Impact VM | @midnightntwrk/onchain-runtime-v4 (current era), @midnight-ntwrk/onchain-runtime-v3 (retained era) |
Midnight.js uses a provider pattern for modularity. Each provider implements an interface from @midnight-ntwrk/midnight-js-types:
interface MidnightProviders {
readonly privateStateProvider: PrivateStateProvider; // Private state storage
readonly publicDataProvider: PublicDataProvider; // Blockchain queries
readonly zkConfigProvider: ZKConfigProvider; // ZK artifact retrieval
readonly proofProvider: ProofProvider; // ZK proof generation
readonly walletProvider: WalletProvider; // Transaction balancing
readonly midnightProvider: MidnightProvider; // Transaction submission
readonly config: MidnightConfig; // Network ID and transaction TTL
readonly contractModuleProvider?: ContractModuleProvider; // Cross-contract call modules
readonly loggerProvider?: LoggerProvider; // Optional logging
}Compiling a Compact smart contract with compactc produces a JavaScript file (circuit execution logic) and a TypeScript declaration file (type definitions). Midnight.js uses these to execute circuits locally and create type-safe transactions. See Contract Compilation Details for more.
The LevelPrivateStateProvider encrypts all private state data at rest using:
| Parameter | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key Derivation | PBKDF2-SHA256 |
| Iterations | 600,000 |
| Password Minimum | 16 characters |
- nvm - Node.js version management
- direnv (optional) - Automatically sets
COMPACTC_VERSION, activates Node via nvm, and configures GPG signing
# If using direnv (sets environment automatically)
direnv allow
# Install dependencies
yarn install
# Build
yarn build
# Test
yarn test
# Lint
yarn lint:fix| Script | Description |
|---|---|
yarn build |
Build all packages |
yarn build:core |
Build everything except testkit-* |
yarn build:testkit |
Build only testkit-* |
yarn clean |
Clean all build outputs |
yarn clean-build |
Clean then rebuild |
| Script | Description |
|---|---|
yarn test |
Run all tests |
yarn test:unit |
Run unit tests (excludes testkit) |
yarn it |
Run integration tests |
yarn e2e |
Run e2e tests |
yarn e2e-single |
Run a single e2e test file |
| Script | Description |
|---|---|
yarn lint |
Run ESLint |
yarn lint:fix |
Fix linting issues |
yarn check |
Type-check + lint + workspace constraints |
yarn check:core |
Same as check but excludes testkit |
yarn typecheck:tests |
Type-check test files |
| Script | Description |
|---|---|
yarn changelog |
Generate/update CHANGELOG.md |
yarn deploy |
Publish all packages |
yarn deploy:core |
Publish core packages (excludes testkit) |
yarn build:markdown-docs |
Generate API docs via TypeDoc |
- DEVELOPMENT.md - Detailed guide for working on packages, debugging, and Turborepo commands
- TROUBLESHOOTING.md - Common issues and solutions
- Vite + WASM Resolution — Fixing duplicate WASM copies in Vite monorepo setups
Branch off main for all new features. Use Conventional Commits:
<type>[optional scope]: <description>
Types: feat, fix, docs, style, refactor, perf, test, chore, ci, build, revert
Scopes: midnight-js, testkit-js, deps, deps-dev, config, release
New functionality must include unit and integration tests (TDD — tests first). See CONTRIBUTING.md for full guidelines.
pre-commit: Runs lint-stagedcommit-msg: Validates commit message formatpre-push: Runsyarn lintandyarn typecheck:tests
| Term | Description |
|---|---|
| Witness | Private computation performed on the end user's device |
| Private State | State updated by witnesses, stored on user's device |
| Circuit | Smart contract function that can be executed and proven |
| ZKIR | Zero-Knowledge Intermediate Representation |
| Current era | The ledger this build runs live. Today it is ledger 9 (v9); at the next hard fork it moves to the new ledger. Names that follow it (CurrentLedgerEra) stay correct across forks |
| Retained era | A ledger before the last hard fork that this build still serves. Today it is ledger 8 (v8); at the next hard fork ledger 9 joins it. Names that follow it (RetainedLedgerEra) stay correct across forks |
Ledger8* / Ledger9* |
Names bound to one ledger. They never change meaning at a fork |
- Preserves contract circuit argument/return types and user-defined types (
PS,Witnesses) throughout the data model - Infers types using
inferinstead of introducing additional generic parameters - Uses least restrictive generic constraints necessary for type safety
- Uses
anyonly for truly unconstrained user-supplied types, not to fix compilation issues - Does not require manual specification of concrete types for generic parameters in most scenarios
- Employs branded types to distinguish domain concepts sharing the same representation (e.g.,
HexStringvsstring)
- Allows custom implementations of API clients via the "provider" pattern
- Collects commonly used types in
@midnight-ntwrk/midnight-js-typesto standardize across applications
- Isomorphic design supporting both browser and Node.js environments
- Supports different Midnight networks (TestNet, MainNet)
- Builds high-level features from low-level utility functions, exporting both
- Provides default implementation for each API client
- Places each API client in a separate package for selective installation
- Minimizes boilerplate required to set up a dApp
- Supplies common sense defaults to avoid application errors
This section assumes familiarity with the Compact language.
When compiling a Compact smart contract with compactc, the JavaScript file contains:
- Execution logic for each circuit in the source contract
- Logic for constructing the contract's initial state
- Utilities for converting on-chain contract state into JavaScript representation
The TypeScript declaration file contains:
- Type definition for the contract (
Contract) - Type definitions for circuits defined within the contract
- Type definition for required witnesses (
Witnesses) - Type definition for on-chain state (
Ledger)
If the Compact source includes witness declarations, the Witnesses type is a non-empty object with a generic type parameter PS representing the private state that witnesses modify during circuit execution.
The term runtime describes the JS executable for a contract, distinct from
@midnight-ntwrk/compact-runtimewhich provides utilities each executable uses.
The Contracts element contains utilities for creating and submitting transactions. All clients are concrete instances of provider interfaces defined in @midnight-ntwrk/midnight-js-types.
Key relationships:
LedgerandCompact Runtimeboth depend onImpact VM(on-chain runtime)Midnight.jsandSubstrate Nodeboth depend onLedger
This allows "rehearsing" circuit calls (running Impact VM) such that execution can be replayed on the node, and creating transactions from rehearsal results that the node accepts.
Releases are prepared by the Release (prepare PR) GitHub Actions workflow
(.github/workflows/release-prepare.yml).
Trigger it manually from the Actions tab with the target version; it bumps versions in
all workspaces, regenerates CHANGELOG.md, and opens a release/v<version> PR. Full CI
runs on that PR and gates the merge. Merging it to main triggers the publish job in the
CI workflow: once the build, unit, integration, and e2e jobs
pass, it publishes packages to GitHub Packages and creates the GitHub Release (which creates
the v<version> tag on the main merge commit). Publishing is gated on the same test run, so
no release can ship ahead of its e2e.
- Actions tab → Release (prepare PR) → Run workflow, from
main. - Enter the
version(e.g.5.1.0or5.1.0-beta.1). - Review the opened
release/v<version>PR (version bumps + changelog), wait for CI, merge.
If CI is unavailable, scripts/release.sh performs the same preparation locally:
# Dry-run (default): updates files only, no git operations — review with `git diff`
./scripts/release.sh 5.1.0
# Only file changes (bump + changelog), no git — what CI runs
./scripts/release.sh 5.1.0 --files-only
# Full local release: bump + changelog + branch + commit + push
./scripts/release.sh 5.1.0 --execute
# Full local release with build and tests beforehand
./scripts/release.sh 5.1.0 --execute --with-testsPrerequisites for the local path: on main with a clean working tree, all previous release
PRs merged, and GPG signing configured. After pushing, open a PR to main; merging it
publishes the release.
When the release PR is merged, the version bump on main triggers the publish job in the CI workflow. Once the build, unit, integration, and e2e jobs pass in the same run, it:
- Extracts release notes from
CHANGELOG.md(section## [<VERSION>]) - Validates and publishes
packages/*to GitHub Packages - Creates a GitHub Release with the extracted notes (creating the
v<VERSION>tag on the merge commit) - Publishes
testkit-js/*if those paths changed
By using this package, you agree to Midnight's Terms and Conditions and Privacy Policy.
Licensed under Apache License 2.0.

