Leo Syntax Cheatsheet
1. File Import
import foo.aleo;
2. Programs
program hello.aleo {
// code
}
3. Primitive Data Types
// Boolean value (true or false)
let b: bool = false;
// Signed 32-bit integer (also available: i8, i16, i64, i128)
let i: i32 = -10i32;
// Unsigned 32-bit integer (also available: u8, u16, u64, u128)
let ui: u32 = 10u32;
// Field element (used in cryptographic computations)
let a: field = 1field;
// Group element (used in elliptic curve operations)
let g: group = 0group;
// Scalar element (used in elliptic curve arithmetic)
let s: scalar = 1scalar;
// Aleo blockchain address
let receiver: address = aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4;
// Digital signature (used for authentication and verification)
let sig: signature = sign1ftal5ngunk4lv9hfygl45z35vqu9cufqlecumke9jety3w2s6vqtjj4hmjulh899zqsxfxk9wm8q40w9zd9v63sqevkz8zaddugwwq35q8nghcp83tgntvyuqgk8yh0temt6gdqpleee0nwnccxfzes6pawcdwyk4f70n9ecmz6675kvrfsruehe27ppdsxrp2jnvcmy2wws6sw0egv;
Type Casting
// Casting between integer types
let a: u8 = 255u8;
let b: u16 = a as u16; // 255u8 to 255u16
let c: u32 = b as u32; // 255u16 to 255u32
let d: i32 = c as i32; // 255u32 to 255i32
// Casting between field and integers
let f: field = 10field;
let i: i32 = f as i32; // Convert field to i32
let u: u64 = f as u64; // Convert field to u64
// Casting between scalar and field
let s: scalar = 5scalar;
let f_from_scalar: field = s as field; // Convert scalar to field
// Casting between group and field
let g: group = group::GEN;
let f_from_group: field = g as field; // Convert group to field
// Address casting (only valid conversions)
let addr: address = aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4;
let addr_field: field = addr as field; // Convert address to field
The primitive types are: address, bool, field, group, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, scalar.
We can cast between all of these types except signature.
Casts between address and field are allowed in both directions; the field → address direction halts at runtime if the field does not encode a valid Aleo address. See Type Casting for the full runtime semantics.
Option Types
// Boolean option value (true or false or none)
let b_some: bool? = true;
let b_none: bool? = none;
// Unwrapping option values
let b_true = b_some.unwrap();
let b_false = b_none.unwrap_or(false);
// Signed 32-bit integer option (also available: i8, i16, i64, i128)
let i_some: i32? = -10i32;
let i_none: i32? = none;
// Unsigned 32-bit integer option (also available: u8, u16, u64, u128)
let ui_some: u32? = 10u32;
let ui_none: u32? = none;
// Field element option (used in cryptographic computations)
let a_some: field? = 1field;
let a_none: field? = none;
// Group element option (used in elliptic curve operations)
let g_some: group? = 0group;
let g_none: group? = none;
// Scalar element option (used in elliptic curve arithmetic)
let s_some: scalar? = 1scalar;
let s_none: scalar? = none;
Both address and signature types do not have option variants.
4. Records
Defining a record:
record Token {
owner: address,
amount: u64,
}
Creating a record:
let user: User = User {
owner: aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4,
balance: 1000u64,
};
Accessing record fields:
let user_address: address = user.owner;
let user_balance: u64 = user.balance;
5. Structs
Defining a struct:
struct Message {
sender: address,
object: u64,
}
Creating an instance of a struct:
let msg: Message = Message {
sender: aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4,
object: 42u64,
};
Accessing struct Fields:
let sender_address: address = msg.sender;
let object_value: u64 = msg.object;
A struct ExternalStruct defined in program external_program.aleo can be referred to outside the program using the syntax external_program.aleo::ExternalStruct.
Const Generics
struct Matrix::[N: u32, M: u32] {
data: [field; N * M],
}
// Usage
fn make_matrix() -> Matrix::[2u32, 2u32] {
return Matrix::[2u32, 2u32] { data: [0field, 1field, 2field, 3field] };
}
Acceptable types for const generic parameters include integer types, bool, scalar, group, field, and address. Generic structs can be declared in a program, in a submodule, or in a library, and can be referenced from any consumer via their fully qualified path.
Option Types
Creating an option type instance of a struct
let point1: Point? = Point {
x: 8u32,
y: 41u32,
};
let point2: Point? = none;
let point1_val = point1.unwrap();
let point2_val = point2.unwrap_or(Point { x: 0u32, y: 0u32 });
Note that because the address and signature types do not have option variants, a struct containing elements of these types also cannot have an option variant.
6. Arrays
Declaring arrays:
let arrb: [bool; 2] = [true, false];
let arr: [u8; 4] = [1u8, 2u8, 3u8, 4u8];
let empty: [u8; 0] = [];
Accessing elements:
let first: u8 = arr[0]; // Get the first element
let second: u8 = arr[1]; // Get the second element
Looping over arrays:
let numbers: [u32; 3] = [5u32, 10u32, 15u32];
let sum: u32 = 0u32;
for i: u8 in 0u8..3u8 {
sum += numbers[i];
}
7. Tuples
Declaring tuples:
// NOTE: Tuples cannot be empty!
let t: (u8, bool, field) = (42u8, true, 100field);
Accessing tuple elements:
// Using de-structuring
let (a, b, c) = t;
//Using index-based accessing
let first: u8 = t.0;
let second: bool = t.1;
let third: field = t.2;
8. Functions
There are three kinds of functions in Leo 4.0:
- Entry
fn(insideprogram {}): the program's public interface, callable from outside. - Helper
fn(outsideprogram {}): private helpers used by entry functions. final fn(outsideprogram {}): reusable finalization logic, inlined intofinal { }blocks at compile time.
Direct/indirect recursive calls are not allowed.
Helper fn
A helper fn is used for computations. Declared outside program {}.
fn compute(a: u64, b: u64) -> u64 {
return a + b;
}
Const Generics
fn sum_first_n_ints::[N: u32]() -> u32 {
let sum = 0u32;
for i: u32 in 0u32..N {
sum += i;
}
return sum;
}
program main.aleo {
fn main() -> u32 {
return sum_first_n_ints::[5u32]();
}
@noupgrade
constructor() {}
}
Acceptable types for const generic parameters include integer types, bool, scalar, group, field, and address.
✅ Can call: helper fn
❌ Cannot call: entry fn
Entry fn
An entry fn is the program's public interface. Declared inside program {}. It can call helper fn and include final { } blocks for on-chain state updates.
fn subtract(a: u64, b: u64) -> u64 {
return a - b;
}
program example.aleo {
fn transfer(receiver: address, amount: u64) {
let balance: u64 = 1000u64;
let new_balance: u64 = subtract(balance, amount);
}
@noupgrade
constructor() {}
}
✅ Can call: helper fn
❌ Cannot call: another entry fn (unless from another program)
Entry fn with final { } (on-chain state)
An entry fn that also modifies public on-chain state returns Final and includes a final { } block.
program example.aleo {
mapping balances: address => u64;
fn mint(receiver: address, amount: u64) -> Final {
return final {
let current_balance: u64 = balances.get_or_use(receiver, 0u64);
balances.set(receiver, current_balance + amount);
};
}
@noupgrade
constructor() {}
}
✅ Can call: helper fn, final fn
❌ Cannot call: another entry fn (unless from another program)
final fn
A final fn contains reusable finalization logic. It is always inlined into the caller's final { } block at compile time. Declared outside program {}.
final fn update_balance(receiver: address, amount: u64) {
let current: u64 = balances.get_or_use(receiver, 0u64);
balances.set(receiver, current + amount);
}
✅ Can call: other final fn
❌ Cannot call: helper fn or entry fn
9. Loops
let count: u32 = 0u32;
for i: u32 in 0u32..5u32 {
count += 1u32;
}
10. Conditionals
let a: u8 = 1u8;
if a == 1u8 {
a += 1u8;
} else if a == 2u8 {
a += 2u8;
} else {
a += 3u8;
}
a = (a == 1u8) ? a + 1u8 : ((a == 2u8) ? a + 2u8 : a + 3u8); // Ternary format
11. Onchain Storage
Mappings
// Querying
let contains_bal: bool = balances.contains(receiver);
let get_bal: u64 = balances.get(receiver);
let get_or_use_bal: u64 = balances.get_or_use(receiver, 0u64);
// Modifying
balances.set(receiver, 100u64);
balances.remove(receiver);
// External mappings (read-only)
let ext_contains: bool = external_program.aleo::balances.contains(receiver);
let ext_get: u64 = external_program.aleo::balances.get(receiver);
let ext_get_or_use: u64 = external_program.aleo::balances.get_or_use(receiver, 0u64);
Storage Variables
// Querying
let unwrap_var: u8 = var.unwrap();
let unwrap_or_var: u8 = var.unwrap_or(0u8);
// Modifying
var = 8u8;
var = none;
// External storage variables (read-only)
let ext_var: u8 = external_program.aleo::var.unwrap();
let ext_var_safe: u8 = external_program.aleo::var.unwrap_or(0u8);
Storage Vectors
// Querying
let len_vec: u32 = vec.len();
let val: u8? = vec.get(idx);
// Modifying
vec.set(idx, value);
vec.push(value);
vec.pop();
vec.swap_remove(idx);
vec.clear();
// External storage vectors (read-only)
let ext_len: u32 = external_program.aleo::vec.len();
let ext_val: u8? = external_program.aleo::vec.get(idx);
12. Operators
Standard
// Arithmetic Operators
let sum: u64 = a + b; // addition (also has wrapped variant)
let diff: u64 = a - b; // subtraction (also has wrapped variant)
let prod: u64 = a * b; // multiplication (also has wrapped variant)
let quot: u64 = a / b; // division (also has wrapped variant)
let power: u64 = a ** (b as u8); // exponentiation (also has wrapped variant)
let remainder: u64 = a % b; // remainder (also has wrapped variant)
let neg: i64 = -(a as i64); // negation
let abs: i64 = neg.abs(); // absolute value (also has wrapped variant)
// Bitwise/Boolean Operators
let logical_and: bool = boolean && boolean; // logical AND
let logical_or: bool = boolean || boolean; // logical OR
let bitwise_and: u64 = a & b; // bitwise AND
let bitwise_or: u64 = a | b; // bitwise OR
let bitwise_xor: u64 = a ^ b; // bitwise XOR
let bitwise_not: u64 = !a; // bitwise NOT
let bitwise_shl: u64 = a << (b as u32); // bitwise shift left (also has wrapped variant)
let bitwise_shr: u64 = a >> (b as u32); // bitwise shift right (also has wrapped variant)
// Comparators
let eq: bool = a == b; // equality
let neq: bool = a != b; // non-equality
let lt: bool = a < b; // less than
let lte: bool = a <= b; // less than or equal
let gt: bool = a > b; // greater than
let gte: bool = a >= b; // greater than or equal
// Group & Field Operators
let g: group = group::GEN; // the group generator
let x: field = 0group.to_x_coordinate(); // x-coordinate of a group element
let y: field = 0group.to_y_coordinate(); // y-coordinate of a group element
let doubled: group = (2group).double(); // Doubles the field/group element
let inverse: field = 1field.inv(); // Multiplicative inverse of the field/group element
let squared: field = 1field.square(); // Square of the field/group element
let root: field = 1field.square_root(); // Square root of the field/group element
// Off-chain context expressions
let this: address = self.address; // Address of program
let caller: address = self.caller; // Address of function caller
let signer: address = self.signer; // Address of tx signer (origin)
// Bit Serialization/Deserialization
let bits: [bool; 90] = Serialize::to_bits(value); // Standard serialization (includes type metadata)
let raw_bits: [bool; 64] = Serialize::to_bits_raw(value); // Raw serialization (no metadata, just raw bits)
// Miscellaneous
assert(boolean); // assert the value of boolean is true
assert_eq(a, b); // assert a and b are equal
assert_neq(a, value); // assert a and value are not equal
let ternary = boolean ? a : b; // Ternary expression
return final {
// On-chain context expressions
let height: u32 = block.height; // Height of current block
let now: i64 = block.timestamp; // Timestamp of current block
let checksum: [u8; 32] = self.checksum; // Checksum of a program
let edition: u16 = self.edition; // Edition of a program
let owner: address = self.program_owner; // Address that deployed a program
};
Cryptographic
// Randomization
let rand: u32 = ChaCha::rand_u32(); // generate a random value `ChaCha::rand_<type>()`
// Hash Functions (BHP, Pedersen, Poseidon, Keccak, SHA3)
let hash: field = BHP256::hash_to_field(1u32); // hash any type to any type
let hash_raw: address = Poseidon2::hash_to_address_raw(1u8); // hash any raw type to any type
let hash_native: [bool; 256] = Keccak256::hash_to_bits(0u64); // hash any type to an array of bits (only available for Keccak and SHA3)
let hash_native_raw: [bool; 256] = Keccak256::hash_to_bits_raw(0u64); // hash any raw type to an array of bits (only available for Keccak and SHA3)
// Commitment Algorithms (BHP, Pedersen)
let commit: group = Pedersen64::commit_to_group(1u32, 1scalar); // commit any type to a field, group, or address, using a scalar as blinding factor (salt)
// Schnorr Signatures
let schnorr: bool = signature::verify(sig, addr, 0field); // Schnorr Signature Verification
// ECDSA Signatures (Keccak, SHA3)
let ecdsa: bool = ECDSA::verify_keccak256(ecdsa_sig, ecdsa_addr, msg); // Verify an ECDSA signature against an ECDSA public key and the Keccak256 hash of a message
let ecdsa_raw: bool = ECDSA::verify_keccak256_raw(ecdsa_sig, ecdsa_addr, msg); // Verify an ECDSA signature against an ECDSA public key and the Keccak256 hash of a raw message
let ecdsa_eth: bool = ECDSA::verify_keccak256_eth(ecdsa_sig, eth_addr, msg); // Verify an ECDSA signature against an Ethereum address and the Keccak256 hash of a raw message
let ecdsa_digest: bool = ECDSA::verify_digest(ecdsa_sig, ecdsa_addr, digest); // Verify an ECDSA signature against an ECDSA public key and a prehashed message
let ecdsa_digest_eth: bool = ECDSA::verify_digest_eth(ecdsa_sig, eth_addr, digest); // Verify an ECDSA signature against an Ethereum address and a prehashed message