Your First Contract
Let’s write a simple counter contract - the “Hello World” of Compact.
Creating the Contract File
Create a new file called counter.compact:
mkdir -p my-first-contract
cd my-first-contract
touch counter.compact
Writing the Contract
Open counter.compact and add the following code:
pragma language_version >= 0.16;
import CompactStandardLibrary;
// Our first ledger state - a counter
export ledger counter: Counter;
// A circuit to increment the counter
export circuit increment(): [] {
counter.increment(1);
}
// A circuit to decrement the counter
export circuit decrement(): [] {
counter.decrement(1);
}
// A circuit to read the current count
export circuit getCount(): Uint<64> {
return counter.read();
}
// A circuit to check if counter is below a threshold
export circuit isLessThan(threshold: Uint<64>): Boolean {
// disclose() is required because comparing a witness value (threshold)
// with ledger state could reveal information about the witness
return counter.lessThan(disclose(threshold));
}
// A circuit to reset the counter
export circuit reset(): [] {
counter.resetToDefault();
}
Let’s break down each part:
Pragma Statement
pragma language_version >= 0.16;
This declares which Compact language versions your code is compatible with. Always include this at the top of your files.
Import Statement
import CompactStandardLibrary;
This imports the Compact standard library, which provides types like Counter, Map, Set, and utility functions.
Ledger Declaration
export ledger counter: Counter;
This declares a public (export) ledger variable named counter of type Counter. Ledger state is stored on the blockchain.
⚠️ CRITICAL: Each ledger field must be declared individually. There is NO block syntax like
ledger { }.
Circuit Declaration
export ledger counter: Counter;
export circuit increment(): [] {
counter.increment(1);
}
Circuits are the functions of Compact. Key points:
exportmakes it callable from outside the contractcircuitis the keyword (notfunction)[]is the return type for circuits that return nothing (notvoidorVoid)- The body contains the operations
The disclose() Function
Notice the isLessThan circuit uses disclose():
export ledger counter: Counter;
export circuit isLessThan(threshold: Uint<64>): Boolean {
return counter.lessThan(disclose(threshold));
}
In Compact, parameters to exported circuits are witness values (private inputs). When you use a witness value in a way that could reveal information about it (like comparing with public ledger state), Compact requires you to explicitly acknowledge this potential disclosure using disclose().
This is a safety feature - Compact won’t let you accidentally leak private data. By calling disclose(), you’re telling the compiler “I understand this value may be revealed, and that’s okay.”
Compiling the Contract
Compile your contract using the compact compile command:
compact compile counter.compact managed/counter
⚠️ Note: The older
compactccommand is deprecated. Always usecompact compile.
If successful, you’ll see a managed output directory structure created (verified with toolchain 0.31.1):
managed/counter/
├── contract/ # TypeScript bindings
│ ├── index.js # JavaScript implementation
│ ├── index.js.map # Source map
│ └── index.d.ts # TypeScript type definitions
├── zkir/ # Circuit representations
│ ├── increment.zkir # One .zkir + .bzkir per circuit
│ └── ...
├── keys/ # Proving and verifying keys
│ ├── increment.prover # One .prover + .verifier per circuit
│ ├── increment.verifier
│ └── ...
└── compiler/
└── contract-info.json # Compiler metadata
Understanding the Output
The compiler generates a managed directory containing:
- contract/ - TypeScript/JavaScript code to interact with the contract
- Deploy the contract
- Call circuit functions
- Read ledger state
- zkir/ - Zero-knowledge intermediate representation of your circuits
- keys/ - Cryptographic keys for proving and verification
- compiler/ - Build metadata and artifacts
Common First-Time Errors
| Error | Cause | Fix |
|---|---|---|
unbound identifier Void | Using Void as return type | Use [] instead |
parse error: found "ledger" | Using ledger { } block syntax | Declare each field separately |
unbound identifier Counter | Missing import | Add import CompactStandardLibrary; |
potential witness-value disclosure must be declared | Using witness value in ledger operation | Wrap the value with disclose() |
Exercises
Complete the exercises in exercises/01_basics/ to practice:
- Writing pragma statements
- Importing the standard library
- Declaring ledger state
- Creating basic circuits
Next Steps
Congratulations! You’ve written and compiled your first Compact contract. In the next chapter, we’ll explore the basic concepts of the language in more detail.