Liquidity is a high-level typed smart-contract language for Tezos that strictly complies with Michelson security restrictions. A formal verification framework for it is under development. See also our prototype of testing framework for Michelson, Techelson.

Try Online About Services Documentation Github Repo

About Liquidity

Liquidity is a high-level language to program Smart Contracts for Tezos. It is a fully typed functional language, it uses the syntaxes of OCaml and ReasonML, and strictly complies with Michelson security restrictions.

A formal-method framework for Liquidity is under development, to prove the correctness of smart-contracts written in Liquidity. (A grant proposal addressed to the Tezos Foundation in Sept 2018 is pending.

The Liquidity language provides the following features:

  • full coverage of the Michelson language: anything that can be written in Michelson can be written in Liquidity,
  • local variables instead of stack manipulations: values can be stored in local variables,
  • high-level types: types like sum-types and record-types can be defined and used in Liquidity programs,
  • a module and contract system to write reusable code and libraries,
  • a powerful type inference mechanism with polymorphism,
  • an alternative ReasonML syntax to write contracts in a Javascript-like language,
  • an efficient and optimizing compiler,
  • a decompiler of Michelson program to Liquidity ones: all Michelson programs can be decompiled into a readable form.

Liquidity already covers 100% of the Michelson features, and contracts generated with Liquidity can be submitted on the current Tezos mainnet, alphanet and zeronet.

The idea of creating a user-friendly language for writing Tezos smart-contracts stemmed in the Summer of 2017, and a very first version was released in October 2017. The team put many efforts to improve the whole framework, and a roadmap was submitted to the Foundation in the form of a grant request in September 2018 (proposal still pending).

Liquidity Online

Liquidity editors will soon be available to program online and submit contracts on Tezos testnet. For now, you can use our Try Liquidity Site to get a taste of the upcoming environment.


Need any services around Tezos Smart contracts? Contact us at OCamlPro!

We can provide the following services:

  • Smart-contract review in-depth analysis
  • Smart-contract development
  • Ad hoc tooling
  • Training sessions
  • Customization of the language / DSL conception

Should you want to use the Liquidity language and associated tooling, please do not hesitate contact us.

Documentation & Examples

Liquidity Documentation is available Here. Examples are also available in the Github project, or in the online editor.

A typical example of a voting smart contract is:

(* Smart contract for voting. Winners of vote split the contract
   balance at the end of the voting period. *)

(** Type of storage for this contract *)
type storage = {
  voters : (address, unit) big_map; (** Used to register voters *)
  votes : (string, nat) map; (** Keep track of vote counts *)
  addresses : (string, key_hash) map; (** Addresses for payout *)
  deadline : timestamp; (** Deadline after which vote closes *)

(** Initial storage *)
let%init storage addresses = {
  (* Initialize vote counts to zero *)
  votes = Map.fold (fun ((name, _kh), votes) ->
      Map.add name 0p votes
    ) addresses Map;
  voters = BigMap ; (* No voters *)
  deadline = Current.time () + 3600 * 24 (* 1 day from now *)

(** Entry point for voting.
    @param choice A string corresponding to the candidate *)
let%entry vote choice storage =
  (* Only allowed while voting period is ongoing *)
  if Current.time () > storage.deadline then failwith "Voting closed";
  (* Voter must send at least 5tz to vote *)
  if Current.amount () < 5.00tz then
    failwith "Not enough money, at least 5tz to vote";
  (* Voter cannot vote twice *)
  if Map.mem (Current.sender ()) storage.voters then
    failwith ("Has already voted", Current.sender ());
  let votes = storage.votes in
  match Map.find choice votes with
  | None ->
    (* Vote must be for an existing candidate *)
    failwith ("Bad vote", choice)
  | Some x ->
    (* Increase vote count for candidate *)
    let storage = storage.votes <- Map.add choice (x + 1p) votes in
    (* Register voter *)
    let storage =
      storage.voters <- Map.add (Current.sender ()) () storage.voters in
    (* Return updated storage *)
    ([], storage)

(* Auxiliary function : returns the list of candidates with the
   maximum number of votes (there can be more than one in case of
   draw). *)
let find_winners votes =
  let winners, _max =
    Map.fold (fun ((name, nb), (winners, max)) ->
        if nb = max then
          name :: winners, max
        else if nb > max then
          [name], nb
        else winners, max
      ) votes ([], 0p) in

(** Entry point for paying winning candidates. *)
let%entry payout () storage =
  (* Only allowed once voting period is over *)
  if Current.time () <= storage.deadline then failwith "Voting ongoing";
  (* Indentify winners of vote *)
  let winners = find_winners storage.votes in
  (* Balance of contract is split equally between winners *)
  let amount = match Current.balance () / List.length winners with
    | None -> failwith "No winners"
    | Some (v, _rem) -> v in
  (* Generate transfer operations *)
  let operations = (fun name ->
      let dest = match Map.find name storage.addresses with
        | None -> failwith () (* This cannot happen *)
        | Some d -> d in
      Account.transfer ~amount ~dest
    ) winners in
  (* Return list of operations. Storage is unchanged *)
  operations, storage

Liquidity Team

Fabrice Le Fessant
Alain Mebsout
David Declerck
Adrien Champion

Liquidity Roadmap

The current roadmap is:

  • Development of an online editor for Liquidity
  • Development of a verification framework for Liquidity contracts


1.0   2019-03-08
    * Revamped module system for function exports
      (This allows to write reusable libraries.)
    * Private functions/values with [@private]
    * Disable inlining per function with [@noinline]
    * Modules and contract aliases

    * Lambdas in constants
    * Better type inference when overloading

    * Fix bad computation of free variables in Contract.create

0.53  2019-02-13
    * Parse ReasonML expressions
    * Option `--call-arg` to print arguments for tezos clients
    * More peephole optimizations

0.52  2019-02-11
    * ReasonML syntax

0.51  2019-02-09
    * Parameterized type definitions

    * Tezos as submodule

    * Bring back support for string amounts, keys, etc.

0.5  2019-01-21
    * Type inference
    * Polymorphic values and polymorphic inference

    * Allow unit patterns `()`
    * Client command `--pack`

    * Fix printing Michelson list and set constants

0.405 2018-12-06
    * Examples of documentation are in the test suite
      (This way, the documentation is always up to date.)
    * Namespacing for externals

    * Fix Travis ubuntu reop
    * Fix parsing of Tezos node json errors
    * Remove unsupported, Set.map_fold

0.404 2018-11-21
    * External custom instructions

    * Support type annotations in expressions
    * Support for digestif 0.7
    * Warning for failing code

    * Bring --no-annot option back
    * Forbid Contract.self in non-inlined functions
    * Prevent redefinition of Map, Set constructors
    * Workaround Micheline pretty-printing bug

0.402 2018-10-17
    * Multiple entry points
    * Encoding of types with Michelson annotations
    * Decompilation of records, enumerations and operations
    * New syntaxes for contract calls
    * Labelled arguments for some built-in functions
    * Contract signatures
    * Instruction Loop.left
    * Compilation of tail-recursive functions
    * Compile multiple contracts in separate files
    * Option `--main` to compile a specific contract

    * Better compilation of wildcard pattern matching
    * Syntax ` addr` for `( addr : C.instance option)
    * Better locations reporting
    * Syntactic sugar `val%entry : param -> _` in signatures
    * Allow empty (non-toplevel) contracts
    * Patterns in entry point arguments
    * Better decompilation

    * Documentation
    * Fix deployment issue with big maps
    * Fix json output of constants
    * Forbid source, sender in initializer

0.37  2018-10-05
    * Compatibility with Tezos's Mainnet/Zeronet/Alphanet
    * Support bytes constants for key, key_hash, address, contract and signature
    * Attribute [@@inlining] to force inlining when not dangerous
    * Command --init-storage (Pierre Michard)
    * Command --inject to inject signed operation

    * Client: Show failing calls and deployments
    * Inline bodies of lambdas
    * Sphinx documentation
    * Easier build with make build-deps
    * More optimizations on Michelson code

    * Fix issue with printing of signatures
    * Missing decompilation for LSL, LSR
    * Fix inlining bug in decompilation
    * Code elimination after FAILWITH in code generation
    * Fix erroneous pretty-printing of tez values
    * Forbid source, sender in initializer

0.32  2018-07-11
    * Compatibility with Tezos's Betanet
    * New calling convention, Contract.transfer returns an operations
      which has to be returned
    * Current.failwith : 'a -> 'b, instead of fail
    * Type bytes, and 0xab... bytes constants
    * Bytes.pack, Bytes.unpack
    * Type address
    * Contract and address constants (KT1..., tz...)
    * Type big_map, and constants BigMap [...], and BigMap.find,
      BigMap.add, BigMap.remove, BigMap.update
    * New compilation of Map/Set/
    * New instruction Map/Set/List.map_fold
    * Current.self renamed to Contract.self
    * New functions Crypto.blake2b/sha256/sha512 instead of Crypto.hash
    * Current.sender
    * Remove Contract.manager
    * (  : p contract option)

    * Better locations reporting
    * Show runtime failures source locations
    * Light type inferrence in specific places
    * Unary negation on int and tez
    * Somewhat better error messages
    * Compile with OCaml 4.06.1
    * Better decompilation naming heuristic

    * Fix issue with decompiling multiple nested failing branches
    * Fix issue with decompilation of ITER
    * Fix some incorrect peephole optimizations
    * Reject maps and sets with non comparable keys

0.14  2018-01-23
    * Support for decompilation of ITER
    * New instructions Map.iter, Set.iter, List.iter
    * Storage initializer
    * Run Liquidity contracts on Tezos node directly (--run)
    * Deploy Liquidity contracts on Tezos node directly (--deploy)
    * Call Liquidity contracts on Tezos node directly (--call)
    * Retrieve Tezos contract storage in Liquidity syntax (--get-storage)

    * Define type abbreviations when decompiling
    * Support timestamp date with Z timezone
    * Encode reduce with closures using ITER
    * Constant of type signature start with backquote and are hex encoded
    * Ready for javascript compilation

    * int and nat are not comparable
    * bool is comparable
    * Division on type tez

0.13  2017-11-30
    * match%nat construct for int -> nat coercion
    * abs is now int -> int
    * Command line options --parse-only, --type-only
    * Command line options --single-line, --compact to output
      Michelson on a single line
    * Support &&, || Boolean connectors
    * Support Map.add, Map.remove, Set.add, Set.remove
    * Lambda types are now written as t1 -> t2 instead of
      (t1, t2) lambda
    * Constant amounts written as litterals: 1.00tz
    * Constant keys and key_hashes as litterals: edpk... tz1...
    * Timestamps as litterals in ISO-8601 format

    * Split typechecking and encoding phases
    * Allow transfers in conditions (for if-then-else and match)
    * Encode to constants when possible
    * Better error messages
    * Accept any name for result of
    * Recover variable names when decompiling

    * Fix bug in encoding of closures
    * Fix bug in compilation of branches with transfers

0.11  2017-10-16
    * Compilation of Liquidity contracts to OCaml bytecode
    * Deconstruct tuples in let, fun and pattern matching
    * Support _ for pattern
    * OCaml syntax for return type of contract's entry point
    * Support for type key_hash

    * Allow compilation without Tezos
    * Better error messages
    * Error location in Michelson source when decompiling
    * Declare type storage when decompiling
    * More efficient encoding of closures

0.1   2017-10-05
    Initial release
© 2017-2019 OCamlPro SAS, All rights reserved.
Get in touch with us
Mentions L├ęgales