Skip to main content
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 enable once 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:

contracts/hello-world.compact
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 message declares public, on-chain state: a field named message that holds a string.
  • export circuit storeMessage declares a circuit, the Compact equivalent of a function that a transaction calls.
  • newMessage is 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 with potential 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.

  1. Start the local network:

    yarn env:up

    This starts a Midnight node, an indexer, and a proof server, and returns once all three are healthy.

  2. Wait for the wallet's DUST to become spendable:

    yarn wait:dust

    A fresh network needs a few seconds before the wallet can pay fees. The script prints DUST ready and exits.

  3. Run the tests:

    yarn test:local

    The 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! 17347ms

    Test Files 1 passed (1)
    Tests 2 passed (2)

    Your contract address and timings differ. If both tests pass, your environment is ready.

  4. Stop the local network:

    yarn env:down

Troubleshooting​

SymptomFix
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 daemonDocker 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 startThe indexer started before the node produced its first block. Run yarn env:up again.
Wallet.InsufficientFunds during the testsThe 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 ClosureNot an error. A connection closes normally during wallet setup, and the run continues.

Next steps​