Using CompactLab
Everything the playground does today, and how to use it.
Introduction
CompactLab is a browser IDE for Compact, the smart-contract language of the Midnight Network. You write a contract in the editor, CompactLab compiles it on its servers, and you deploy and call it from your own 1AM wallet on Midnight Preprod or Preview.
The playground runs Compact compiler 0.31.1 (language 0.23), following Midnight’s support matrix. It is free and open source.
Quick start
- Open the playground and create a free account with email, or with Google or GitHub when offered.
- Create a project from the Counter template.
- Install 1AM, switch it to Preprod and get test tokens from the Preprod faucet.
- Click Deploy and approve in 1AM. Unedited Hello World and Counter projects deploy with bundled keys; anything else needs Compile first.
- In the Contract panel, call
increment()and watch theroundledger value change.
Editor and projects
The editor is Monaco with Compact support: syntax highlighting, completions, hover types, lint and inline diagnostics. A project can hold several files and folders. Projects are saved to your account as you type, so you can continue on another device.
If you lose your connection, changes are kept in the browser and synced when you’re back. To keep a local copy, use Export project (.zip). The zip includes your sources, the deployment details and, when present, the compiled ZK files.
Compile
Compile (⌘↵) sends the source to the CompactLab API, which runs the pinned compiler and produces the circuits and proving keys Deploy needs. The result shows the compiler version, the number of circuits and any warnings.
Typecheck only, in the menu next to Compile, runs the compiler without generating proving keys. It is faster and good for catching errors, but it doesn’t prepare a Deploy.
Deploy and call
Deploy opens 1AM. If the constructor takes arguments, the playground asks for them first, with inputs that match their Compact types. Your wallet builds the proof, pays the fee and submits the transaction.
After a deploy, the Contract panel lists each exported circuit with typed inputs. Calling a circuit opens 1AM again; when the transaction lands, the panel shows the new public ledger values. Use Join another contract to work with an address deployed earlier from the same contract.
If your contract declares witnesses, the playground asks for values only for the witnesses that the constructor or circuit actually calls. Witness values are saved to your project by default; turn off Sync witness values to the cloud in Settings to keep them on your device only.
Privacy view
The Privacy view marks each value in your contract as private, public, disclosed or hashed, so you can see where data crosses from private to public and where disclose() is needed. It is a guide built from the source, not a formal verifier: the compiler is the final word.
Templates
Every template is compiled against the pinned compiler before a release. Hello World and Counter ship with prebuilt keys, so an unedited copy deploys without compiling. The Learn panel explains the concepts each one uses.
| Template | What it shows | Level |
|---|---|---|
| Hello World | Store a public message on-chain. Deploys instantly. | beginner |
| Counter | On-chain counter with the built-in Counter type. Deploys instantly. | beginner |
| Simple Voting | One vote per wallet; why a witness that drives a public tally must be disclosed. | beginner |
| Bulletin Board | One-slot board: post and take down with a ZK ownership proof. | beginner |
| Owned Contract | Admin pattern with a transferable owner role. | beginner |
| Fungible Token | Token balances in a Map keyed by ZswapCoinPublicKey. | intermediate |
| Name Registry | Map names to owners: register, transfer, release. | intermediate |
| Player Profile | Struct types and a Map of structs. | intermediate |
| Escrow | State-machine escrow with an arbiter and role guards. | intermediate |
| ZK Commitment | Commit-reveal: prove knowledge of a secret without revealing it. | advanced |
There is also a Blank Contract with an inline guide.
1AM wallet
Deploy and Call need the 1AM browser wallet. Install it, unlock it, select the same network as the playground and approve the playground site when asked. Other Midnight wallets are not supported yet. You can write, compile and typecheck without a wallet.
Networks and fees
- Preprod is the default. Get tNIGHT from the Preprod faucet.
- Preview runs the same toolchain. Get tNIGHT from the Preview faucet.
Fees are paid in tDUST, which your wallet generates from tNIGHT. If your tDUST balance is 0, fund tNIGHT and wait a moment before you deploy. Contracts deployed on one network are not visible on the other.
Reading ledger state
The playground reads public ledger values from the Midnight indexer. On Preprod it goes through CompactLab, which shares a daily quota between users. If that quota runs out, add your own free Blockfrost key in Settings, or switch to Preview, which uses Midnight’s public indexer.
Contract structure
A contract starts with a language pragma and the standard library import. It then declares ledger state (public, on-chain), optional witnesses, an optional constructor and the circuits people can call. This is the Hello World template:
pragma language_version >= 0.23; import CompactStandardLibrary; // Public on-chain state, readable by anyone.export ledger message: Opaque<"string">; // Anyone can update the message. Writing a circuit// parameter to the ledger requires disclose().export circuit storeMessage(newMessage: Opaque<"string">): [] { message = disclose(newMessage);}Each exported circuit becomes a provable transition: callers prove they ran it correctly, and only the resulting public state changes are recorded.
Witnesses and disclose()
A witness is private input that the caller supplies when building the proof. It never goes on-chain by itself. Anything derived from a witness or a circuit parameter must pass through disclose() before it can be written to the ledger or returned, and the compiler rejects code that forgets it. From the Bulletin Board template:
// The poster's secret key stays on their device.witness localSecretKey(): Bytes<32>; export circuit post(newMessage: Opaque<"string">): [] { assert(state == State.VACANT, "Board is occupied"); // Only a hash of the secret becomes public. owner = disclose(boardPublicKey(localSecretKey(), sequence as Field as Bytes<32>)); message = disclose(some<Opaque<"string">>(newMessage)); state = State.OCCUPIED;}persistentHash gives the same result across compiler versions, so use it for anything you store and compare later, like owner keys. transientHash is cheaper but not guaranteed to stay stable across upgrades, so don’t use it to derive stored state.
Built-in types
| Counter | Ledger counter: .increment(n), .read() |
| Uint<N> | Unsigned integer of N bits, e.g. Uint<64> |
| Field | Element of the proof system's prime field |
| Boolean | true or false |
| Bytes<N> | Fixed-length byte array |
| Opaque<"string"> | A string the contract stores but doesn't inspect |
| Maybe<T> | Optional value: some<T>(v) or none<T>() |
| Vector<N, T> | Fixed-length array of N values |
| Map<K, V>, Set<T> | Ledger collections |
| ZswapCoinPublicKey | A Midnight wallet public key |
For the full language, see the Compact reference.
Limits
- Mainnet is not supported. Audit any contract independently before it handles real value.
- 1AM is the only supported wallet.
- There is no local simulator yet: circuits run on-chain, on Preprod or Preview.
- Fees are paid from your own wallet. Sponsored fees are planned, not available.
- Compiling and reading Preprod state are rate-limited per account.
FAQ
Does CompactLab see my private inputs?
Proofs are built by 1AM in your browser. Witness values are saved with your project unless you turn off cloud sync for them in Settings. Your contract source is sent to the API to compile.
Why does Deploy say I need to compile?
Deploy needs proving keys that match your current source. Run Compile (not Typecheck only) after your last edit. Unedited Hello World and Counter projects are the exception.
Where do I report a bug?
Open an issue on GitHub.