Skip to main content

Layout of a Leo Project

Manifest

program.json is the Leo manifest file that configures our package.

program.json
{
"program": "hello.aleo",
"version": "0.1.0",
"description": "",
"license": "MIT",
"dependencies": null,
"dev_dependencies": null
}

The program ID in program is the official name that other developers will be able to look up after you have published your program.

    "program": "hello.aleo",

Dependencies will be added to the field of the same name, as they are added. The dependencies are also pegged in the leo.lock file.

The src/ directory is where all of your Leo code will live. The main entry point of your project is a file in this directory appropriately named main.leo. Calls to many of the Leo CLI commands will require you to have this file within your project in order to succeed properly.

Programs

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} { ... }. The body of the program is delimited by curly braces {}.

main.leo
import foo.aleo;

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

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

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

async transition mint_public(
public receiver: address,
public amount: u64,
) -> (Token, Future) {
return (Token {
owner: receiver,
amount,
}, update_state(receiver, amount));
}

async function update_state(
public receiver: address,
public amount: u64,
) {
let current_amount: u64 = Mapping::get_or_use(account, receiver, 0u64);
Mapping::set(account, receiver, current_amount + amount);
}

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

The following must be declared inside the scope of a program in a Leo file:

The following must be declared outside the scope of a program in a Leo file:

Declarations are locally accessible within a program file. If you need a declaration from another Leo file, you must import it.

Imports

You can import dependencies that are downloaded to the imports directory. An import is declared as import {filename}.aleo; The dependency resolver will pull the imported program from the network or the local filesystem.

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

program hello.aleo { }

Program ID

A program ID is declared as {name}.{network}.

The first character of a name must be a lowercase letter. name can only contain lowercase letters, numbers, and underscores, and must not contain a double underscore (__) or the keyword aleo in it.

Currently, aleo is the only supported network domain.

program hello.aleo; // valid

program Foo.aleo; // invalid
program baR.aleo; // invalid
program 0foo.aleo; // invalid
program 0_foo.aleo; // invalid
program _foo.aleo; // invalid
program foo__bar.aleo; // invalid
program aleo.aleo; // invalid

Modules

In addition to your main file, Leo also supports a module system as of v3.2.0.

Leaf modules (i.e. modules without submodules) must be defined in a single file (ex. foo.leo). Modules with submodules must be defined by an optional top-level .leo file and a subdirectory directory containing the submodules:

Take the following project as an example:

src
├── common.leo
├── main.leo
├── outer.leo
└── outer
└── inner.leo

Given the structure above, the following modules are defined:

FilenameTypeModule NameAccess Location & Pattern
common.leoModulecommonmain.leo : common::<item>
outer.leoModuleoutermain.leo : outer::<item>
outer/inner.leoSubmoduleouter::innermain.leo : outer::inner::<item>
outer.leo : inner::<item>
info

Only relative paths are implemented so far. That means that items in outer.leo cannot be accessed from items in inner.leo, for example. This is limiting for now but will no longer be an issue when we add absolute paths.

A module file may only contain struct, const, and inline definitions:

const X: u32 = 2u32;

struct S {
a: field
}

inline increment(x: field) -> field {
return 1field;
}