# Leo Language Guide

### Statically Typed​

Leo is a statically typed language, which means we must know the type of each variable before executing a circuit.

### Explicit Types Required​

There is no `undefined` or `null` value in Leo. When assigning a new variable, the type of the value must be explicitly stated.

### Pass by Value​

Expressions in Leo are always passed by value, which means their values are always copied when they are used as function inputs or in right sides of assignments.

## Data Types and Values​

### Booleans​

Leo supports the traditional `true` or `false` boolean values. The explicit `bool` type for booleans in statements is required.

``let b: bool = false;``

### Integers​

Leo supports signed integer types `i8`, `i16`, `i32`, `i64`, `i128` and unsigned integer types `u8`, `u16`, `u32`, `u64`, `u128`.

``let b: u8 = 1u8;``

Underscores `_` can be used to separate digits in integer literals.

``let b: u8 = 1_000_000u64;``
info

Higher bit length integers generate more constraints in the circuit, which can slow down computation time.

#### A Note on Leo Integers​

Leo will not default to an integer type. The definition of an integer must include an explicit type.
Type casting is supported as of Leo v1.8.2

``let a: u8 = 2u8; // explicit typelet b: u16 = a as u16; // type castinglet c: u8 = 2; // implicit type -- not supported``

### Field Elements​

Leo supports the `field` type for elements of the base field of the elliptic curve. These are unsigned integers less than the modulus of the base field. The following are the smallest and largest field elements.

``let a: field = 0field;let b: field = 8444461749428370424248824938781546531375899335154063827935233455917409239040field;``

### Group Elements​

The set of affine points on the elliptic curve forms a group. The curve is a Twisted Edwards curve with `a = -1` and `d = 3021`. Leo supports a subgroup of the group, generated by a generator point, as a primitive data type. A group element is denoted by the x-coordinate of its point; for example, `2group` means the point `(2, 5553594316923449299484601589326170487897520766531075014687114064346375156608)`.

``let a: group = 0group; // the point with 0 x-coordinate, (0, 1)let b: group = 1540945439182663264862696551825005342995406165131907382295858612069623286213group;  // the generator point``

The aforementioned generator point can be obtained via a constant associated to the `group` type.

``let g: group = group::GEN; // the group generator``

### Scalar Elements​

Leo supports the `scalar` type for elements of the scalar field defined by the elliptic curve subgroup. These are unsigned integers less than the modulus of the scalar field. The following are the smallest and largest scalars.

``let a: scalar = 0scalar;let b: scalar = 2111115437357092606062206234695386632838870926408408195193685246394721360382scalar;``

Addresses are defined to enable compiler-optimized routines for parsing and operating over addresses. These semantics will be accompanied by a standard library in a future sprint.

``let receiver: address = aleo1ezamst4pjgj9zfxqq0fwfj8a4cjuqndmasgata3hggzqygggnyfq6kmyd4;``

### Signatures​

Aleo uses the Schnorr signature scheme to sign messages with an Aleo private key. Signatures are a native type in Leo, and can be declared with the keyword `signature`. Signatures can be verified in Leo using the `signature::verify` or `s.verify` operators.

``program test.aleo {    struct foo {        a: u8,        b: scalar    }    transition verify_field(s: signature, a: address, v: field) {        let first: bool = signature::verify(s, a, v);        let second: bool = s.verify(a, v);        assert_eq(first, second);    }    transition verify_foo(s: signature, a: address, v: foo) {        let first: bool = signature::verify(s, a, v);        let second: bool = s.verify(a, v);        assert_eq(first, second);    }}``

## Layout of a Leo Program​

A Leo program contains declarations of a Program, Constants, Imports , Transition Functions, Async Functions, Helper Functions, Structs , Records, and Mappings. Declarations are locally accessible within a program file. If you need a declaration from another Leo file, you must import it.

### Program​

A program is a collection of code (its functions) and data (its types) that resides at a program ID on the Aleo blockchain. A program is declared as `program {name}.{network} { ... }`. The body of the program is delimited by curly braces `{}`.

``import foo.aleo;program hello.aleo {    mapping balances: address => u64;    record token {        owner: address,        amount: u64,    }    struct message {        sender: address,        object: u64,    }    async transition mint_public(        public receiver: address,        public amount: u64,    ) -> (token, Future) {        return (token {            owner: receiver,            amount,        }, update_state(receiver, amount));    }    async function update_state(        public receiver: address,        public amount: u64,    ) {        let current_amount: u64 = Mapping::get_or_use(account, receiver, 0u64);        Mapping::set(account, receiver, current_amount + amount);   }    function compute(a: u64, b: u64) -> u64 {        return a + b;    }}``

The following must be declared inside the scope of a program in a Leo file:

• constants
• mappings
• record types
• struct types
• transition functions
• helper functions
• async functions

The following must be declared outside the scope of a program in a Leo file:

• imports

#### Program ID​

A program ID is declared as `{name}.{network}`. The first character of a `name` must be a lowercase letter. `name` can contain lowercase letters, numbers, and underscores. Currently, `aleo` is the only supported `network` domain.

``program hello.aleo; // validprogram Foo.aleo;   // invalidprogram baR.aleo;   // invalidprogram 0foo.aleo;  // invalidprogram 0_foo.aleo; // invalidprogram _foo.aleo;  // invalid``

### Constant​

A constant is declared as `const {name}: {type} = {expression};`.
Constants are immutable and must be assigned a value when declared.
Constants can be declared in the global scope or in a local function scope.

``program foo.aleo {    const FOO: u8 = 1u8;        function bar() -> u8 {        const BAR: u8 = 2u8;        return FOO + BAR;    }}``

### Import​

You can import dependencies that are downloaded to the `imports` directory. An import is declared as `import {filename}.aleo;` The dependency resolver will pull the imported program from the network or the local filesystem.

``import foo.aleo; // Import all `foo.aleo` declarations into the `hello.aleo` program.program hello.aleo { }``

### Struct​

A struct data type is declared as `struct {name} {}`. Structs contain component declarations `{name}: {type},`.

``struct array3 {    a0: u32,    a1: u32,    a2: u32,}``

### Record​

A record data type is declared as `record {name} {}`. Records contain component declarations `{visibility} {name}: {type},`.

A visibility can be either `constant`, `public`, or `private`. Users may also omit the visibility, in which case, Leo will default to `private`.

Record data structures must contain the `owner` component as shown below. When passing a record as input to a program function, the `_nonce: group` component is also required (but it does not need to be declared in the Leo program).

``record token {    // The token owner.    owner: address,    // The token amount.    amount: u64,}``

### Array​

Leo supports static arrays. Array types are declared as `[type; length]` and can be nested. Arrays cannot be empty nor modified.

Arrays only support constant accesses. The accessor expression must be a constant integer.

Arrays can contain primitive data types, structs, or arrays. Structs and records can also contain arrays.

Arrays can be iterated over using a for loop.

``// Initalize a boolean array of length 4let arr: [bool; 4] = [true, false, true, false];// Nested arraylet nested: [[bool; 2]; 2] = [[true, false], [true, false]];struct bar {    data: u8,}// Array of structslet arr_of_structs: [bar; 2] = [bar { data: 1u8 }, bar { data: 2u8 }];// Access the field of a struct within an arraytransition foo(a: [bar; 8]) -> u8 {    return a[0u8].data;}// Struct that contains an arraystruct bat {    data: [u8; 8],}// Record that contains an arrayrecord floo {    owner: address,    data: [u8; 8],}// Declare a mapping that contains array valuesmapping data: address => [bool; 8];// Iterate over an array using a for loop and sum the values withintransition sum_with_loop(a: [u64; 4]) -> u64 {    let sum: u64 = 0u64;    for i: u8 in 0u8..4u8 {        sum += a[i];    }    return sum;}``

### Tuple​

Leo supports tuples. Tuple types are declared as `(type1, type2, ...)` and can be nested. Tuples cannot be empty or modified.

Tuples only support constant access with a dot `.` and a constant integer.

Tuples can contain primitive data types, structs, or arrays. Structs and records can also contain tuples.

``program test.aleo {    transition baz(foo: u8, bar: u8) -> u8 {        let a: (u8, u8) = (foo, bar);        let result: u8 = a.0 + a.1;        return result;    }}``

### Transition Function​

Transition functions in Leo are declared as `transition {name}() {}`. Transition functions can be called directly when running a Leo program (via `leo run`). Transition functions contain expressions and statements that can compute values. Transition functions must be in a program's current scope to be called. Transition functions that call async functions to execute code on-chain must be declared as `async transition`.

``program hello.aleo {    transition foo(        public a: field,        b: field,    ) -> field {        return a + b;    }}``

#### Function Inputs​

A function input is declared as `{visibility} {name}: {type}`. Function inputs must be declared just after the function name declaration, in parentheses.

``// The transition function `foo` takes a single input `a` with type `field` and visibility `public`.transition foo(public a: field) { }``

#### Function Outputs​

A function output is calculated as `return {expression};`. Returning an output ends the execution of the function. The return type of the function declaration must match the type of the returned `{expression}`.

``transition foo(public a: field) -> field {    // Returns the addition of the public input a and the value `1field`.    return a + 1field;}``

### Helper Function​

A helper function is declared as `function {name}({arguments}) {}`. Helper functions contain expressions and statements that can compute values, however helper functions cannot produce `records`.

Helper functions cannot be called directly. Instead, they must be called by other functions. Inputs of helper functions cannot have `{visibility}` modifiers like transition functions, since they are used only internally, not as part of a program's external interface.

``function foo(    a: field,    b: field,) -> field {    return a + b;}``

### Inline Function​

An inline function is declared as `inline {name}() {}`. Inline functions contain expressions and statements that can compute values. Inline functions cannot be executed directly from the outside, instead the Leo compiler inlines the body of the function at each call site.

Inputs of inline functions cannot have `{visibility}` modifiers like transition functions, since they are used only internally, not as part of a program's external interface.

``inline foo(    a: field,    b: field,) -> field {    return a + b;}``

The rules for functions (in the traditional sense) are as follows:

• There are three variants of functions: `transition`, `function`, `inline`.
• A `transition` can only call a `function`, `inline`, or external `transition`.
• A `function` can only call an `inline`.
• An `inline` can only call another `inline`.
• Direct/indirect recursive calls are not allowed.

### Async Function​

An async function is declared as `async function` and is used to define computation run on-chain. A call to an async function returns a `Future` object. It is asynchronous because the code gets executed at a later point in time. One of its primary uses is to initiate or change public on chain state within mappings. An async function can only be called by an async transition function and is executed on chain, after the zero-knowledge proof of the execution of the associated transition is verified. Async functions are atomic; they either succeed or fail, and the state is reverted if they fail.

An example of using an async function to perform on-chain state mutation is in the `transfer_public_to_private` transition below, which updates the public account mapping (and thus a user's balance) when called.

``program transfer.aleo {    // The function `transfer_public_to_private` turns a specified token amount    // from `account` into a token record for the specified receiver.    //    // This function preserves privacy for the receiver's record, however    // it publicly reveals the sender and the specified token amount.    async transition transfer_public_to_private(        receiver: address,        public amount: u64    ) -> (token, Future) {        // Produce a token record for the token receiver.        let new: token = token {            owner: receiver,            amount,        };        // Return the receiver's record, then decrement the token amount of the caller publicly.        return (new, update_public_state(self.caller, amount));    }    async function update_public_state(        public sender: address,        public amount: u64    ) {        // Decrements `account[sender]` by `amount`.        // If `account[sender]` does not exist, it will be created.        // If `account[sender] - amount` underflows, `transfer_public_to_private` is reverted.        let current_amount: u64 = Mapping::get_or_use(account, sender, 0u64);        Mapping::set(account, sender, current_amount - amount);    }}``

If there is no need to create or alter the public on-chain state, async functions are not required.

### Mappings​

A mapping is declared as `mapping {name}: {key-type} => {value-type}`. Mappings contain key-value pairs. Mappings are stored on chain.

``// On-chain storage of an `account` mapping,// with `address` as the type of keys,// and `u64` as the type of values.mapping account: address => u64;``

#### Mapping Operations​

Mappings can be read from and modified by calling one of the following functions.

#### get​

A get command, e.g. `current_value = Mapping::get(counter, addr);` Gets the value stored at `addr` in `counter` and stores the result in `current_value` If the value at `addr` does not exist, then the program will fail to execute.

#### get_or_use​

A get command that uses the provided default if the key is not present in the mapping,
e.g. `let current_value: u64 = Mapping::get_or_use(counter, addr, 0u64);`
Gets the value stored at `addr` in `counter` and stores the result in `current_value`. If the key is not present, `0u64` is stored in `counter` (associated to the key) and in `current_value`.

#### set​

A set command, e.g. `Mapping::set(counter, addr, current_value + 1u64);` Sets the `addr` entry as `current_value + 1u64` in `counter`.

#### contains​

A contains command, e.g. `let contains: bool = Mapping::contains(counter, addr);` Returns `true` if `addr` is present in `counter`, `false` otherwise.

#### remove​

A remove command, e.g. `Mapping::remove(counter, addr);` Removes the entry at `addr` in `counter`.

#### Usage​

info

Mapping operations are only allowed in an async function.

``program test.aleo {    mapping counter: address => u64;    async transition dubble() -> Future {        return update_mappings(self.caller);    }    async function update_mappings(addr: address) {        let current_value: u64 = Mapping::get_or_use(counter, addr, 0u64);        Mapping::set(counter, addr, current_value + 1u64);        current_value = Mapping::get(counter, addr);        Mapping::set(counter, addr, current_value + 1u64);    }}``

## Control Structures​

### If Statements​

If statements are declared as `if {condition} { ... } else if {condition} { ... } else { ... }`. If statements can be nested.

``    let a: u8 = 1u8;        if a == 1u8 {        a += 1u8;    } else if a == 2u8 {        a += 2u8;    } else {        a += 3u8;    }``

### Return Statements​

Return statements are declared as `return {expression};`.

``    let a: u8 = 1u8;        if a == 1u8 {        return a + 1u8;    } else if a == 2u8 {        return a + 2u8;    } else {        return a + 3u8;    }``

### For Loops​

For loops are declared as `for {variable: type} in {lower bound}..{upper bound}`. The loop bounds must be integer constants of the same type. Furthermore, the lower bound must be less than the upper bound. Nested loops are supported.

``  let count: u32 = 0u32;  for i: u32 in 0u32..5u32 {    count += 1u32;  }  return count; // returns 5u32``

## Operators​

Operators in Leo compute a value based off of one or more expressions. Leo defaults to checked arithmetic, which means that it will throw an error if an overflow or division by zero is detected.

For instance, addition adds `first` with `second`, storing the outcome in `destination`. For integer types, a constraint is added to check for overflow. For cases where wrapping semantics are needed for integer types, see the wrapped variants of the operators.

``let a: u8 = 1u8 + 1u8;// a is equal to 2a += 1u8;// a is now equal to 3a = a.add(1u8);// a is now equal to 4``

See the Operator Reference for a complete list of operators.

### Operator Precedence​

Operators will prioritize evaluation according to:

OperatorAssociativity
`!` `-`(unary)
`**`right to left
`*` `/`left to right
`+` `-`(binary)left to right
`<<` `>>`left to right
`&`left to right
`|`left to right
`^`left to right
`<` `>` `<=` `>=`
`==` `!=`left to right
`&&`left to right
`||`left to right
`=` `+=` `-=` `*=` `/=` `%=` `**=` `<<=` `>>=` `&=` `|=` `^=`

### Parentheses​

To prioritize a different evaluation, use parentheses `()` around the expression.

``let result = (a + 1u8) * 2u8;``

`(a + 1u8)` will be evaluated before multiplying by two `* 2u8`.

## Context-dependent Expressions​

Leo supports several expressions that can be used to reference information about the Aleo blockchain and the current transaction.

### self.caller​

Returns the address of the account/program that invoked the current `transition`.

``program test.aleo {    transition matches(addr: address) -> bool {        return self.caller == addr;    }}``

### self.signer​

Returns the address of the account that invoked that top-level `transition`. This is the account that signed the transaction.

``program test.aleo {    transition matches(addr: address) -> bool {        return self.signer == addr;    }}``

### block.height​

Returns the height of the current block.

info

`block.height` is only allowed in an async function.

``program test.aleo {    async transition matches(height: u32) -> Future {        return check_block_height(height);    }         async function check_block_height(height: u32) {        assert_eq(height, block.height);    }}``

## Core Functions​

Core functions are functions that are built into the Leo language. They are used to check assertions and perform cryptographic operations such as hashing, commitment, and random number generation.

### Assert and AssertEq​

`assert` and `assert_eq` are used to verify that a condition is true. If the condition is false, the program will fail.

``program test.aleo {    transition matches() {        assert(true);        assert_eq(1u8, 1u8);    }}``

### Hash​

Leo supports the following hashing algorithms: `BHP256`, `BHP512`, `BHP768`, `BHP1024`, `Pedersen64`, `Pedersen128`, `Poseidon2`, `Poseidon4`, `Poseidon8`, `Keccak256`, `Keccak384`, `Keccak512`, `SHA3_256`, `SHA3_384`, `SHA3_512`.
The output type of a hash function is specified in the function name. e.g. `hash_to_group` will return a `group` type. Hash functions take any type as an argument.

``let a: scalar = BHP256::hash_to_scalar(1u8);let b: address = Pedersen64::hash_to_address(1u128);let c: group = Poseidon2::hash_to_group(1field);``

See all hash functions

### Commit​

Leo supports the following commitment algorithms: `BHP256`, `BHP512`, `BHP768`, `BHP1024`, `Pedersen64`, `Pedersen128`
The output type of a commitment function is specified in the function name. e.g. `commit_to_group` will return a `group` type.
The first argument can be any type. The second argument must be a `field` type and is used as a blinding factor.

``let a: group = BHP256::commit_to_group(1u8, 2field);let b: address = Pedersen64::commit_to_address(1u128, 2field);``

See all commitment functions

### Random​

Leo supports the `ChaCha` random number generation algorithm.
The output type of a random function is specified in the function name. e.g. `rand_group` will return a `group` type.

info

Random functions are only allowed in an async function.

``let a: group = ChaCha::rand_group();let b: u32 = ChaCha::rand_u32();``

See all random functions

## Deprecated Syntax​

#### Increment and Decrement​

`increment()` and `decrement()` functions are deprecated as of Leo v1.7.0. Please use the `Mapping::set()` function instead.

#### Finalize​

`finalize` and the associated programming model is deprecated as of Leo v2.0.0. Please use an `async function` instead.