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 testing framework for Michelson, Techelson.

Try Online About Services Documentation Github Repo

Announcement


Free/Libre Open Source License

Two weeks ago, OCamlPro announced that we would move to a license-based model for Liquidity, the smart contract language for Tezos that we developed since June 2017 without any external funding. This move was motivated by the unsustainability of Liquidity maintenance by OCamlPro alone, and our decision not to rely on the Tezos Foundation anymore for funding tied to unacceptable conditions/requests. Since then, we have been contacted by many users of Liquidity, who had started developing smart contracts with Liquidity and could not find any language with the same feature set and quality.

We have decided today to open-source Liquidity. From now on, it will be distributed under a GPLv3 free software license. This switch will help all the members of the Tezos ecosystem to build even more interesting dapps. We hope to be able to find new funding in the future to support further development of Liquidity and related projects.

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, which the Foudation unfortunately decided not to support.

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.

Services


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

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;
  addresses;
  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
  winners

(** 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 = List.map (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

Changelog


1.0   2019-03-08
    NEW FEATURES
    * 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

    IMPROVEMENTS
    * Lambdas in constants
    * Better type inference when overloading

    BUG FIXES
    * Fix bad computation of free variables in Contract.create

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

0.52  2019-02-11
    NEW FEATURES
    * ReasonML syntax

0.51  2019-02-09
    NEW FEATURES
    * Parameterized type definitions

    IMPROVEMENTS
    * Tezos as submodule

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

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

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

    BUG FIXES
    * Fix printing Michelson list and set constants

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

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

0.404 2018-11-21
    NEW FEATURES
    * External custom instructions

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

    BUG FIXES
    * 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
    NEW FEATURES
    * 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

    IMPROVEMENTS
    * Better compilation of wildcard pattern matching
    * Syntax `C.at addr` for `(Contact.at 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

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

0.37  2018-10-05
    NEW FEATURES
    * 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

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

    BUG FIXES
    * 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
    NEW FEATURES
    * 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/List.map
    * 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
    * (Contract.at  : p contract option)

    IMPROVEMENTS
    * 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

    BUG FIXES
    * 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
    NEW FEATURES
    * 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)

    IMPROVEMENTS
    * 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

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

0.13  2017-11-30
    NEW FEATURES
    * 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

    IMPROVEMENTS
    * 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 Contract.call
    * Recover variable names when decompiling

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

0.11  2017-10-16
    NEW FEATURES
    * 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

    IMPROVEMENTS
    * 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