Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/provablehq/leo/llms.txt

Use this file to discover all available pages before exploring further.

Learn how to manage dependencies, configure your program manifest, and work with local and network imports in Leo.

Program Manifest (program.json)

Every Leo project has a program.json manifest file that defines metadata and dependencies.

Basic Structure

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

Manifest Fields

The unique name of your program, must end with .aleo:
"program": "token_manager.aleo"
Must follow naming rules:
  • Start with a letter
  • Contain only ASCII alphanumeric characters and underscores
  • Not contain the keyword “aleo” in the name
  • Not be a reserved keyword
Semantic version of your program:
"version": "0.1.0"
Follows Semantic Versioning: MAJOR.MINOR.PATCH
Human-readable description of your program:
"description": "A token management system for Aleo"
Software license identifier:
"license": "MIT"
Common options: MIT, Apache-2.0, GPL-3.0
External programs your code depends on:
"dependencies": [
  {
    "name": "credits.aleo",
    "location": "network"
  }
]
Dependencies only needed for testing:
"dev_dependencies": [
  {
    "name": "test_utils.aleo",
    "location": "local",
    "path": "../test_utils"
  }
]

Dependency Types

Network Dependencies

Import deployed programs from the Aleo network:
"dependencies": [
  {
    "name": "credits.aleo",
    "location": "network"
  }
]
Use in your code:
src/main.leo
import credits.aleo;

program my_program.aleo {
    fn use_credits() {
        // Access credits.aleo functions
    }
}
Network dependencies are automatically fetched from the Aleo network and cached locally in ~/.aleo/registry/{network}/{program}/{edition}/.

Local Dependencies

Import programs from your local filesystem:
"dependencies": [
  {
    "name": "utils.aleo",
    "location": "local",
    "path": "../utils"
  }
]
Project structure:
workspace/
├── my_program/
│   ├── program.json
│   └── src/
│       └── main.leo
└── utils/
    ├── program.json
    └── src/
        └── main.leo
Local dependencies use relative paths from the project directory. Use ../ to reference sibling directories.

Local Aleo File Dependencies

Import compiled .aleo bytecode files:
"dependencies": [
  {
    "name": "external.aleo",
    "location": "local",
    "path": "./imports/external.aleo"
  }
]
The path must point to a .aleo file, not a directory. This is useful for pre-compiled dependencies.

Dependency Editions

Specify a specific version (edition) of a network dependency:
"dependencies": [
  {
    "name": "token.aleo",
    "location": "network",
    "edition": 2
  }
]
Without an edition, Leo fetches the latest version:
{
  "name": "token.aleo",
  "location": "network"
  // Fetches latest edition
}

Dependency Resolution

Leo resolves dependencies in topological order:
1

Parse Manifest

Leo reads program.json and collects all dependencies:
let manifest = Manifest::read_from_file(path.join(MANIFEST_FILENAME))?;
2

Build Dependency Graph

Constructs a directed graph of program dependencies:
let mut digraph = DiGraph::<Symbol>::new(Default::default());
3

Fetch Dependencies

For each dependency:
  • Local: Read from filesystem
  • Network: Fetch from Aleo network and cache
Program::fetch(name_symbol, edition, home_path, network, endpoint, no_cache)
4

Topological Sort

Orders programs so dependencies are compiled before dependents:
let ordered_dependency_symbols = 
    digraph.post_order()
           .map_err(|_| UtilError::circular_dependency_error())?;

Circular Dependency Detection

Leo detects and rejects circular dependencies:
program_a.aleo → program_b.aleo → program_a.aleo  // Error!
Circular dependencies are not allowed. Restructure your programs to have a directed acyclic dependency graph.

Working with Imports

Basic Import

Import an external program:
import credits.aleo;

program my_program.aleo {
    fn transfer_credits() {
        // Use credits.aleo functions
    }
}

Using Imported Types

Access structs and records from imported programs:
import child.aleo;

program parent.aleo {
    fn create_foo() -> child.aleo/Foo {
        return child.aleo/Foo {
            x: 0u32,
            y: 0u32
        };
    }
}

Calling Imported Functions

Invoke functions from dependencies:
import registry.aleo;

program relay.aleo {
    fn check_user(addr: address) -> Final {
        return final { finalize_check(addr); };
    }
}

final fn finalize_check(addr: address) {
    // Access external mapping
    let is_registered: bool = Mapping::get(registry.aleo/users, addr);
    assert_eq(is_registered, true);
}
Use the program.aleo/item syntax to reference imported items.

Package Structure

Leo expects a specific directory structure:
my_program/
├── program.json          # Manifest
├── src/                  # Source code
│   ├── main.leo         # Main program
│   └── utils.leo        # Additional modules (optional)
├── tests/               # Test files
│   └── test_*.leo
├── build/               # Build output
│   ├── main.aleo        # Compiled program
│   └── imports/         # Compiled dependencies
│       └── credits.aleo
├── outputs/             # Compiler artifacts
│   └── *.ast
└── .gitignore

Creating Packages

Initialize a new package:
leo new my_program
This creates:
  • Package directory structure
  • program.json with defaults
  • Basic src/main.leo template
  • .gitignore file
  • Sample test file
From crates/package/src/package.rs:420-442:
fn main_template(name: &str) -> String {
    format!(
        r#"// The '{name}' program.
program {name}.aleo {{
    @noupgrade
    constructor() {{}}

    fn main(public a: u32, b: u32) -> u32 {{
        let c: u32 = a + b;
        return c;
    }}
}}
"#
    )
}

Package Constants

Key directory names (from crates/package/src/lib.rs):
pub const SOURCE_DIRECTORY: &str = "src";
pub const MAIN_FILENAME: &str = "main.leo";
pub const IMPORTS_DIRECTORY: &str = "build/imports";
pub const OUTPUTS_DIRECTORY: &str = "outputs";
pub const BUILD_DIRECTORY: &str = "build";
pub const ABI_FILENAME: &str = "abi.json";
pub const TESTS_DIRECTORY: &str = "tests";

Dependency Caching

Cache Location

Network dependencies are cached in:
~/.aleo/registry/{network}/{program}/{edition}/{program}.aleo
Example:
~/.aleo/registry/testnetv0/credits.aleo/0/credits.aleo

Disabling Cache

Force fresh dependency fetches:
leo build --no-cache

Clearing Cache

Manually clear cached dependencies:
rm -rf ~/.aleo/registry
The cache improves build times by avoiding redundant network requests. Only disable it when debugging dependency issues.

Advanced Configuration

Multiple Dependencies

Combine local and network dependencies:
"dependencies": [
  {
    "name": "credits.aleo",
    "location": "network"
  },
  {
    "name": "utils.aleo",
    "location": "local",
    "path": "../utils"
  },
  {
    "name": "token.aleo",
    "location": "network",
    "edition": 3
  }
]

Test Dependencies

Separate dependencies for tests:
"dependencies": [
  {
    "name": "credits.aleo",
    "location": "network"
  }
],
"dev_dependencies": [
  {
    "name": "test_helpers.aleo",
    "location": "local",
    "path": "../test_helpers"
  }
]
Test files automatically have access to both dependencies and dev_dependencies.

Dependency Conflicts

Leo detects conflicting dependencies:
// Error: Same program with different configurations
"dependencies": [
  {
    "name": "token.aleo",
    "location": "network",
    "edition": 1
  },
  {
    "name": "token.aleo",
    "location": "network",
    "edition": 2  // Conflict!
  }
]
Each dependency must have a unique name and consistent configuration across your dependency tree.

Program Size Limits

Programs have size limits (from crates/package/src/lib.rs):
pub const MAX_PROGRAM_SIZE: usize = 
    <snarkvm::prelude::TestnetV0 as snarkvm::prelude::Network>::MAX_PROGRAM_SIZE;
Leo validates program size during compilation:
if program_size > MAX_PROGRAM_SIZE {
    return Err(UtilError::program_size_limit_exceeded(
        name,
        program_size,
        MAX_PROGRAM_SIZE,
    ));
}

Best Practices

1

Use Semantic Versioning

Version your programs following semver:
"version": "1.2.3"  // MAJOR.MINOR.PATCH
  • MAJOR: Breaking changes
  • MINOR: New features, backwards compatible
  • PATCH: Bug fixes
2

Pin Critical Dependencies

Specify editions for production dependencies:
"dependencies": [
  {
    "name": "critical.aleo",
    "location": "network",
    "edition": 5  // Pin to specific version
  }
]
3

Document Dependencies

Add descriptions explaining why dependencies are needed:
{
  "program": "my_program.aleo",
  "description": "Token manager - depends on credits.aleo for transfers",
  "dependencies": [{
    "name": "credits.aleo",
    "location": "network"
  }]
}
4

Minimize Dependencies

Only include necessary dependencies to reduce:
  • Build times
  • Circuit size
  • Attack surface
5

Test Dependency Changes

Run full test suite after updating dependencies:
leo test
leo build

Troubleshooting

Dependency Not Found

Error: Failed to fetch program from network
Solutions:
  • Verify program name spelling
  • Check network connectivity
  • Confirm program exists on network
  • Try with --no-cache

Path Not Found

Error: Failed to load package at './relative/path'
Solutions:
  • Verify relative path is correct
  • Check directory structure
  • Ensure program.json exists in target directory

Version Mismatch

Error: Program name mismatch
Solutions:
  • Ensure program field in manifest matches actual program name
  • Check imported program names match manifest declarations

Next Steps

Build docs developers (and LLMs) love