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:

  • export makes it callable from outside the contract
  • circuit is the keyword (not function)
  • [] is the return type for circuits that return nothing (not void or Void)
  • 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 compactc command is deprecated. Always use compact 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:

  1. contract/ - TypeScript/JavaScript code to interact with the contract
    • Deploy the contract
    • Call circuit functions
    • Read ledger state
  2. zkir/ - Zero-knowledge intermediate representation of your circuits
  3. keys/ - Cryptographic keys for proving and verification
  4. compiler/ - Build metadata and artifacts

Common First-Time Errors

ErrorCauseFix
unbound identifier VoidUsing Void as return typeUse [] instead
parse error: found "ledger"Using ledger { } block syntaxDeclare each field separately
unbound identifier CounterMissing importAdd import CompactStandardLibrary;
potential witness-value disclosure must be declaredUsing witness value in ledger operationWrap the value with disclose()

Exercises

Complete the exercises in exercises/01_basics/ to practice:

  1. Writing pragma statements
  2. Importing the standard library
  3. Declaring ledger state
  4. 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.