Skip to main content
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