Skip to main content

Multi-File Projects

Real contracts span multiple files. Ora supports file-level modules with namespace-qualified imports.

Importing files

comptime const math = @import("./math.ora");

contract Calculator {
pub fn run() -> u256 {
return math.add(40, 2);
}
}

@import("./math.ora") loads math.ora from the same directory. All public items in the imported file are accessible through the math namespace.

Project configuration: ora.toml

Every Ora project has an ora.toml at its root:

schema_version = "0.1"

[compiler]
output_dir = "./artifacts"

[[targets]]
name = "Main"
kind = "contract"
root = "contracts/main.ora"
  • schema_version — config format version (currently "0.1")
  • [compiler].output_dir — where build artifacts go
  • [[targets]] — one or more compilation targets, each with a name, kind ("contract" or "library"), and root source file

Constructor arguments

If your contract has a pub fn init(...) constructor, pass initial values via init_args:

schema_version = "0.1"

[compiler]
output_dir = "./artifacts"
init_args = ["initial_counter=0"]

[[targets]]
name = "Main"
kind = "contract"
root = "contracts/main.ora"

Create a project with ora init:

ora init my-vault

This generates:

my-vault/
├── ora.toml
├── contracts/
│ └── main.ora
└── README.md

Project structure

A typical Ora project:

my-vault/
├── ora.toml
├── contracts/
│ ├── main.ora # Entry point
│ ├── vault.ora # Vault logic
│ ├── errors.ora # Error declarations
│ └── types.ora # Shared types
└── tests/
└── vault_test.ora

Splitting the vault

errors.ora:

error InsufficientBalance(required: u256, available: u256);
error ZeroAmount;

types.ora:

struct DepositRecord {
amount: u256;
timestamp: u256;
}

enum VaultStatus : u8 {
Active = 0,
Paused = 1,
Closed = 2
}

vault.ora:

comptime const std = @import("std");
comptime const errors = @import("./errors.ora");
comptime const types = @import("./types.ora");

contract Vault {
storage var totalDeposits: u256 = 0;
storage var balances: map<address, u256>;
storage var status: types.VaultStatus;

pub fn deposit(amount: MinValue<u256, 1>) -> !bool | errors.ZeroAmount {
if (amount == 0) { return errors.ZeroAmount; }
let sender: address = std.msg.sender();
balances[sender] += amount;
totalDeposits += amount;
return true;
}
}

Imported names are always qualified: errors.ZeroAmount, types.VaultStatus. No implicit flattening.

Building

ora build contracts/vault.ora

The compiler resolves imports relative to the source file. The ora.toml configures the entry point and output directory for the full project build.

Further reading