Skip to main content
Version: master

Structure of a Leo Program

Layout of a Leo Program

A Leo program contains declarations of a Program, Constants, Imports , Structs, Records, Mappings, Interfaces, and functions. Declarations are locally accessible within a program file. If you need a declaration from another Leo file, you must import it.

Program

A program is a collection of code (its functions) and data (its types) that resides at a program ID on the Aleo blockchain. A program is declared as program {name}.{network} { ... }, with the body delimited by curly braces.

For the canonical list of which declarations belong inside vs. outside the program { ... } block, the program-ID naming rules, and import semantics, see Project Layout.

import foo.aleo;

const FOO: u64 = 1u64;

struct Message {
sender: address,
object: u64,
}

fn compute(a: u64, b: u64) -> u64 {
return a + b + FOO;
}

program hello.aleo {
mapping account: address => u64;

record Token {
owner: address,
amount: u64,
}

fn mint_public(
public receiver: address,
public amount: u64,
) -> (Token, Final) {
let token: Token = Token { owner: receiver, amount };
return (token, final {
let current_amount: u64 = Mapping::get_or_use(account, receiver, 0u64);
Mapping::set(account, receiver, current_amount + amount);
});
}

@noupgrade
constructor() {}
}

Constant

A constant is declared as const {name}: {type} = {expression};. Constants are immutable, and the right-hand side must be an expression evaluatable at compile time.

Constants can be declared in four scopes:

  • Global scope (outside the program block in main.leo): accessible anywhere in the same file.
  • Program scope (inside a program block, outside any function): accessible within that program.
  • Local scope (inside a function body): accessible only within that function.
  • Module scope (any non-main.leo source file in the package; module files do not contain a program block and may only declare const, struct, fn, and interface): accessible within the same package via path::to::module::CONST_NAME. See Modules for details.

Constants are also supported in libraries, which are separate packages containing reusable code. A library's root file and its submodules may declare constants, accessible from any dependent package as library::CONST_NAME or library::path::to::submodule::CONST_NAME.

Accessibility across packages: Global and program-scope constants in a program are accessible from other programs that import it, using program_name.aleo::CONST_NAME. Constants declared in a submodule of an imported program are reachable through their full module path — program_name.aleo::path::to::submodule::CONST_NAME — provided the dependency is compiled from Leo source (pre-compiled .aleo stubs do not carry the submodule type information needed for resolution).

const MAX: u64 = 100u64; // global constant

program constants_demo.aleo {
const MULTIPLIER: u64 = 2u64; // program-scope constant

fn compute(x: u64) -> u64 {
const OFFSET: u64 = 5u64; // local constant
return x * MULTIPLIER + OFFSET;
}

@noupgrade
constructor() {}
}

Supported types: All integer types (u8, u16, u32, u64, u128, i8, i16, i32, i64, i128), bool, field, group, scalar, address, and tuples, arrays, and structs composed of these types.

Compile-time expressions: The right-hand side of a constant declaration must be evaluatable at compile time. Valid right-hand sides include:

  • Literal values (e.g., 42u32, true, 1field)
  • References to previously declared constants
  • Arithmetic, bitwise, and comparison expressions over constants (e.g., MAX * 2u64, !FLAG)
  • Tuple, array, and struct expressions whose components are themselves compile-time constants
const BASE: u32 = 10u32;
const LIMIT: u32 = BASE * 5u32; // expression over constants
const PAIR: (u32, bool) = (LIMIT, true); // tuple constant

Import

An import is declared as import {filename}.aleo;. The dependency resolver pulls the imported program from the network or the local imports/ directory. See Imports for the declaration syntax and the Dependencies guide for resolution rules.

import foo.aleo; // Import all `foo.aleo` declarations into the `hello.aleo` program.

program hello.aleo {

Mappings

A mapping is declared as mapping {name}: {key-type} => {value-type}. Mappings contain key-value pairs and are stored on chain.

// On-chain storage of an `account` mapping,
// with `address` as the type of keys,
// and `u64` as the type of values.
mapping account: address => u64;

Storage

A storage variable is declared as storage {name}: {type}. Storage variables contain singleton values. They are declared at program scope and are stored on chain, similar to mappings.

// On-chain storage of an `counter` storage variable of type u32,
storage counter: u32;

A storage vector is declared as storage {name}: [{type}]. Storage vectors contain dynamic lists of values of a given type. They are declared at program scope and are stored on chain, similar to mappings.

// On-chain storage of an `accounts` storage vector of type address,
storage accounts: [address];

Struct

A struct data type is declared as struct {name} {}. Structs contain component declarations {name}: {type},.

struct Array3 {
a0: u32,
a1: u32,
a2: u32,
}

Record

A record data type is declared as record {name} {}. A record name must not contain the keyword aleo, and must not be a prefix of any other record name declared in the same program (the check does not extend across imported programs). This is a snarkVM requirement.

Records contain component declarations {visibility} {name}: {type},. Names of record components must not contain the keyword aleo.

The visibility qualifier may be specified as constant, public, or private. If no qualifier is provided, Leo defaults to private.

Record data structures must always contain a component named owner of type address, as shown below. When passing a record as input to a program function, the _nonce: group and _version: u8 components are also required but do not need to be declared in the Leo program. They are inserted automatically by the compiler.

record Token {
// The token owner.
owner: address,
// The token amount.
amount: u64,
}