Program Structure

Every Compact program follows a consistent structure. Understanding this structure helps you write well-organized contracts.

Basic Structure

A typical Compact file has these sections:

// 1. Pragma declaration (required, must be first)
pragma language_version >= 0.16;

// 2. Imports (must come after pragma)
import CompactStandardLibrary;

// 3. Type definitions (structs, enums)
export struct Person {
    name: Bytes<32>,
    age: Uint<8>
}

export enum Status { Active, Inactive }

// 4. Ledger state declarations
export ledger registry: Map<Bytes<32>, Person>;
export ledger count: Counter;

// 5. Witness declarations
witness getSecretKey(): Bytes<32>;

// 6. Circuits
export circuit register(name: Bytes<32>, age: Uint<8>): [] {
    // implementation
}

pure circuit validateAge(age: Uint<8>): Boolean {
    return age >= 18;
}

Note: The pragma must come first, followed by imports. Beyond that, the ordering of types, ledger, witnesses, and circuits is flexible—the order shown above is a recommended convention for readability, not a language requirement.

Modules and Imports

Compact supports modules for organizing code. You can import from modules using path syntax:

import CompactStandardLibrary;
import "mymodule/helpers";

Note: The path import is shown for illustration — it only compiles if mymodule/helpers.compact actually exists in your project.

For details on modules and the import mechanism, see the Modules chapter.

Naming Conventions

While Compact doesn’t enforce naming conventions, following consistent patterns improves readability. This book uses:

ElementConventionExample
CircuitscamelCasegetBalance, transferFunds
Types/StructsPascalCasePerson, PlayerStats
Ledger fieldscamelCasebalance, totalSupply
Enum variantslowercaseStatus.pending, State.active
ConstantscamelCasemaxValue, threshold

Note: These are conventions used throughout this book for consistency. Choose a style that works for your project and apply it consistently.

Visibility

The export keyword controls what’s accessible from outside:

// Public - accessible from TypeScript/externally
export ledger balance: Counter;
export circuit deposit(): [] { }

// Private - only accessible within the contract
ledger internal_state: Field;
circuit helper(): Field {
    return internal_state;
}

Exported circuits become entry points callable from TypeScript. Exported ledger fields are visible via the generated ledger() function.

Key Points

  1. Pragma first, imports second - These must come at the top in this order
  2. One declaration per line - No block syntax for ledger (e.g., no ledger { })
  3. Export consciously - Only expose what needs to be public
  4. Organize logically - Group related code together (recommended, not required)