Skip to main content
Learn how to write well-structured Leo programs following best practices and conventions used in the Leo compiler codebase.

Project Structure

A Leo project follows a standardized directory structure:

Creating a New Project

Use the Leo CLI to initialize a new project:
This creates a project structure with a basic program template:
src/main.leo
The @noupgrade annotation prevents program upgrades after deployment. Other options include @admin(address="...") and @checksum(mapping="...", key="...") for controlled upgrades.

Program Manifest (program.json)

The program.json file defines your project metadata and dependencies:
program.json

Key Fields

  • program: Must match your program name and end with .aleo
  • version: Semantic version of your program
  • dependencies: External programs your code imports
  • dev_dependencies: Dependencies only needed for tests
The Leo version is automatically tracked and doesn’t need manual specification.

Code Organization

Function Definitions

Leo supports multiple function types:
1

Regular Functions

Standard functions that execute on-chain:
2

Async Functions with Finalize

Functions that return Final for on-chain finalization:
3

Final Functions

Finalize functions that run in on-chain execution:

Data Structures

Structs

Define custom data types:

Records

Private data structures with ownership:
Records must have an owner field of type address. They represent private, owned assets.

Mappings

On-chain key-value storage:

Arrays and Tuples

Fixed-size Arrays

Nested Arrays

Tuples

Loops

Leo supports range-based for loops:
Loops are unrolled at compile time. Keep iteration counts reasonable to avoid excessive code generation.

Naming Conventions

Program Names

Program names must:
  • End with .aleo
  • Start with a letter (not underscore or number)
  • Contain only ASCII alphanumeric characters and underscores
  • Not contain the keyword aleo in the name itself
  • Not be a SnarkVM reserved keyword
Invalid names:
  • _myprogram.aleo (starts with underscore)
  • 123program.aleo (starts with number)
  • my-program.aleo (contains hyphen)
  • myaleo.aleo (contains “aleo”)

Variable and Function Names

Follow snake_case conventions:

Importing Dependencies

Local Dependencies

Import from local packages:

Network Dependencies

Import from the Aleo network:
Network dependencies are automatically fetched and cached in ~/.aleo/registry/.

Special Variables

Leo provides special context variables:
  • self.signer - The address that signed the transaction
  • self.caller - The program that called this function

Best Practices Summary

1

Use Type Annotations

Always specify types explicitly for clarity:
2

Initialize Before Use

Declare and initialize variables together:
3

Use Meaningful Names

Choose descriptive names for functions and variables:
4

Document Complex Logic

Add comments for non-obvious code:

Next Steps

Now that you understand Leo program structure: