Version: 0.17.0


LIGO is strongly and statically typed. This means that the compiler checks how your contract processes data, ensuring that each function's expectations are met. If it passes the test, your contract will not fail at run-time due to some inconsistent assumptions on your data. This is called type checking.

LIGO types are built on top of Michelson's type system.

Built-in types#

For quick reference, you can find all the built-in types here.

Type aliases#

Type aliasing consists of renaming a given type when the context calls for a more precise name. This increases readability and maintainability of your smart contracts. For example we can choose to alias a string type as an animal breed - this will allow us to comunicate our intent with added clarity.

type breed is string
const dog_breed : breed = "Saluki"

The above type definitions are aliases, which means that breed and string are interchangable in all contexts.

Simple types#

// The type account_balances denotes maps from addresses to tez
type account_balances is map (address, tez)
const ledger : account_balances =
map [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez]

Structured types#

Often contracts require complex data structures, which in turn require well-typed storage or functions to work with. LIGO offers a simple way to compose simple types into structured types.

The first of those structured types is the record, which aggregates types as fields and indexes them with a field name. In the example below you can see the definition of data types for a ledger that keeps the balance and number of previous transactions for a given account.

// Type aliasing
type account is address
type number_of_transactions is nat
// The type account_data is a record with two fields.
type account_data is record [
balance : tez;
transactions : number_of_transactions
// A ledger is a map from accounts to account_data
type ledger is map (account, account_data)
const my_ledger : ledger = map [
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ->
record [
balance = 10mutez;
transactions = 5n

Complementary to records are the variant types, which are described in the section on pattern matching. Records are a product of types, while variant types are sums of types.


In certain cases, the type of an expression cannot be properly inferred by the compiler. In order to help the type checker, you can annotate an expression with its desired type. Here is an example:

type parameter is Back | Claim | Withdraw
type storage is
owner : address;
goal : tez;
deadline : timestamp;
backers : map (address, tez);
funded : bool
type return is list (operation) * storage
function back (var action : unit; var store : storage) : return is
if now > store.deadline then
failwith ("Deadline passed.")
else case store.backers[sender] of
None -> store.backers[sender] := amount
| Some (x) -> skip
end with ((nil : list (operation)), store) // Annotation