For the complete documentation index, see llms.txt
Create your first Midnight contract
This quick start checks that your environment can compile and test a Compact contract, and shows you what Compact looks like. You write a contract that stores a message, compile it, and run the tests from the example-hello-world repository against a local network. You don't need test tokens or a public network.
Prerequisites
- The Compact toolchain, version 0.31.1. Follow Install the toolchain, then confirm with
compact compile --version. The support matrix lists the versions that each network supports. - Node.js 22 or 24. The project's dependencies don't install on Node.js 23.
- Yarn. Run
corepack enableonce and the project uses the Yarn version it pins. - Docker Desktop, or another Docker engine with Compose v2, running.
- Git.
Set up the project
Clone the repository and install its dependencies:
git clone https://github.com/midnightntwrk/example-hello-world.git
cd example-hello-world
yarn install
Run every later command from the example-hello-world directory.
Write the contract
Create contracts/hello-world.compact with this content:
pragma language_version 0.23;
export ledger message: Opaque<"string">;
export circuit storeMessage(newMessage: Opaque<"string">): [] {
message = disclose(newMessage);
}
pragma language_version 0.23;declares the Compact language version. The compiler rejects the file if it doesn't support that version.export ledger messagedeclares public, on-chain state: a field namedmessagethat holds a string.export circuit storeMessagedeclares a circuit, the Compact equivalent of a function that a transaction calls.newMessageis private, because circuit arguments are private by default.disclose()tells the compiler that you intend to make it public by storing it on the ledger. Without it, compiling fails withpotential witness-value disclosure must be declared but is not.
Compile the contract
Run the compile script:
yarn compile
The script runs compact compile contracts/hello-world.compact contracts/managed/hello-world. The compiler turns the contract into a zero-knowledge circuit, generates its proving and verifying keys, and writes the JavaScript and TypeScript that DApp code imports. It reports one circuit:
Compiling 1 circuits:
circuit "storeMessage" (k=6, rows=26)
Overall progress [====================] 1/1
If you run the command from a script or a coding agent, you see only the first line. The files below are the check that counts.
The output lands in contracts/managed/hello-world:
contracts/managed/hello-world/
├── compiler/ contract-info.json, a description of the contract for other tools
├── contract/ index.js and index.d.ts, the contract implementation
├── keys/ storeMessage.prover and storeMessage.verifier
└── zkir/ storeMessage.zkir and storeMessage.bzkir, the circuit in ZKIR
Test the contract on a local network
The repository's tests deploy your compiled contract, call storeMessage with Hello World!, and read the message back from the ledger. They run against a local network in Docker, with a wallet that the network funds when it starts.
-
Start the local network:
yarn env:upThis starts a Midnight node, an indexer, and a proof server, and returns once all three are healthy.
-
Wait for the wallet's DUST to become spendable:
yarn wait:dustA fresh network needs a few seconds before the wallet can pay fees. The script prints
DUST readyand exits. -
Run the tests:
yarn test:localThe run ends with both tests passing:
[11:53:49.163] INFO (63955): Wallet sync complete after 23 emissions[11:53:49.174] INFO (63955): Providers initialized on 'local'. Ready to test![11:53:49.177] INFO (63955): Creating private state...[11:54:09.014] INFO (63955): Setting the contract address...[11:54:09.014] INFO (63955): Contract deployed at: 2e5b69411fcabd3e803fd2971fdae2ffe4482e7f3d0174d50b8a4f0f3abb29fb✓ src/test/hw.test.ts (2 tests) 37897ms✓ Deploys the contract 19846ms✓ Stores Hello World! 17347msTest Files 1 passed (1)Tests 2 passed (2)Your contract address and timings differ. If both tests pass, your environment is ready.
-
Stop the local network:
yarn env:down
Troubleshooting
| Symptom | Fix |
|---|---|
language version X.Y.Z mismatch (X.Y.Z is your compiler's language version, for example 0.22.0) | Your compiler doesn't support the contract's language version. Install toolchain 0.31.1 with compact update 0.31.1. |
Cannot find module './managed/hello-world/contract/index.js' | The contract isn't compiled. Run yarn compile before the tests. |
Cannot connect to the Docker daemon | Docker isn't running. Start Docker Desktop, wait for the engine to come up, then rerun yarn env:up. |
yarn env:up fails with container ...-indexer-1 exited (1) on the first start | The indexer started before the node produced its first block. Run yarn env:up again. |
Wallet.InsufficientFunds during the tests | The tests ran before the wallet's DUST was spendable. Run yarn wait:dust, then rerun yarn test:local. |
RPC-CORE: subscribeRuntimeVersion(): RuntimeVersion:: disconnected ... 1000:: Normal Closure | Not an error. A connection closes normally during wallet setup, and the run continues. |
Next steps
- Make your contract private: this contract stores its message in public. Keep a contract's inputs off the ledger with witnesses and commitments.
- Bulletin board tutorial: build a complete DApp, from a contract with private state to a CLI.
- Deploying and operating a contract: write the TypeScript that deploys and calls a contract.
- Networks and environments: move from the local network to Preprod.
- Compact language reference: the full language.
- Examples: complete DApps to read and run.