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 s: 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 = 1group;
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.
You can cast an address to a field but not vice versa.
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
let m = Matrix::[2, 2] { data: [0, 1, 2, 3] };
Note that generic structs cannot currently be imported outside a program, but can be declared and used in submodules. Acceptable types for const generic parameters include integer types, bool, scalar, group, field, and address.
Option Types
Creating an option type instance of a struct
struct Point {
x : u32,
y : u32
}
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 in 0u32..N {
sum += i;
}
return sum;
}
program main.aleo {
fn main() -> u32 {
return sum_first_n_ints::[5u32]();
}
}
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);
}
}
✅ 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);
};
}
}
✅ 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
mapping balances: address => u64;
// 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
storage var: u8;
// 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
storage vec: [u8];
// 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; // 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 = a && b; // logical AND
let logical_or: bool = a || b; // 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 // bitwise shift left (also has wrapped variant)
let bitwise_shr: u64 = a >> b // 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 = 1field.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
// Context-dependent Expressions
let height: u32 = block.height; // Height of current block
let now: i64 = block.timestamp; // Timestamp of current block
let this: address = self.address; // Address of program
let caller: address = self.caller; // Address of function caller
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
let signer: address = self.signer; // Address of tx signer (origin)
// Bit Serialization/Deserialization
let bits: [bool; 58] = Serialize::to_bits(value); // Standard serialization (includes type metadata)
let raw_bits: [bool; 32] = Serialize::to_bits_raw(value); // Raw serialization (no metadata, just raw bits)
// Miscellaneous
assert(a); // assert the value of a is true
assert_eq(a, b); // assert a and b are equal
assert_neq(b, c); // assert b and c are not equal
let ternary = boolean ? a : b; // Ternary expression
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(0field); // 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(0field); // 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(1u64, 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(sig, 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(sig, 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(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(sig, addr, digest); // Verify an ECDSA signature against an ECDSA public key and a prehashed message
let ecdsa_digest_eth: bool = ECDSA::verify_digest_eth(sig, eth_addr, digest); // Verify an ECDSA signature against an Ethereum address and a prehashed message