Decentralized App development on Tezos for beginners part 3

Use Liquidity to easily write Michelson contracts

catsigma
5 min readMar 2, 2018

Updated @ 2018.03.02 (outdated, waiting for the formal release of zeronet)

Series parts

  1. Set up your own Tezos node and learn the basics concepts.
  2. Meet the Tezos smart contract language — Michelson.
  3. Use Liquidity to easily write Michelson contracts.
  4. Let’s deploy our first Michelson contract on Tezos.
  5. Build a website to interact with our Tezos contract.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Previously we have mentioned that Michelson is not suitable for handwriting, so in this part, we’ll introduce a high-level language called Liquidity which can compile to Michelson.

Installation

Luckily, the mini version of Liquidity can be successfully compiled under both macOS and Windows. But you still need to do some tricks to accomplish this. Everyone hates to get stuck at compiling tools, and I know that, so we won’t cover compiling tutorials and the binaries are provided here. Or an online version is available here.

Basics

let%entry main
(parameter : unit)
(storage : unit)
: unit * unit =
((), storage)

Liquidity inherits the grammar style from OCaml and it is basically isomorphic to Michelson. We’ll learn this piece of code line by line.

let%entry main means this function is used as the Michelson contract code body. (parameter : unit) means the input-type of this contract is unit , and (storage : unit) means the storage-type is unit. Then : unit * unit is a return-type. In Michelson the return-type should be a pair combines one any type and the storage type, so : unit * unit means a unit real return-type combined with a storage-type and * just means the combination of a pair. ((), storage) is the return data. () in Liquidity means UNIT and (a, b) construct a pair.

So what if we want to return a natural number?

let%entry main
(parameter : unit)
(storage : unit)
: nat * unit =
(0p, storage)

Using a number with p at the suffix with a type nat to represent a natural number. Here are all the types supported by Liquidity.

One more step further, let’s do some basic arithmetic.

How to do addition?

let%entry main
(parameter : nat * nat)
(storage : unit)
: nat * unit =
let (a, b) = parameter in
(a + b, storage)

In this piece of function, we input two natural numbers and return the addition of them. let (a, b) = parameter in means to deconstruct the pair and assign the two elements to a and b. You can easily replace + to * but not - and /.

For safety reason, Liquidity force you to handle different conditions in some operation.

How to do subtraction of nat :

let%entry main
(parameter : nat * nat)
(storage : unit)
: nat * unit =
let (a, b) = parameter in
match%nat a - b with
| Plus x -> (x, storage)
| Minus _ -> Current.fail ()

a-b will get you an int type. So we should use a match%nat to detect if the int is positive or negative. The syntax of this is called pattern matching. You will get used to this because pattern matching is heavily used in Liquidity to keep safe. Current.fail () means FAIL in Michelson, this instruction will break the calling chain and revert the current transaction.

Current.fail () is really weird, isn’t it? Liquidity’s function application syntax inherits from OCaml, so instead of using A(x, y, z) , it uses A x y z and A () when there is no parameter needed.

How to do division of nat :

let%entry main
(parameter : nat * nat)
(storage : unit)
: nat * unit =
let (a, b) = parameter in
let result =
match a / b with
| None -> Current.fail ()
| Some x -> x.(0) (* <- the x is (quotient, remainder) *)
in
(result, storage)

We use two let definitions here and connect them by in.

Types

type method =
| Addition
| Multiplication
| Subtraction
| Division

This looks like an enumerated type, but it’s actually not. Liquidity uses ADT(Algebraic Data Types) to enable a more powerful combination of types. Types of Liquidity can be constructed like the codes below.

(* this is a record type *)
type division_t = {
dividend : nat;
divisor : nat;
quotient : nat option;
remainder : nat option;
}
type method =
| Addition of nat * nat
| Multiplication of tez * tez (* tez is the type for XTZ *)
| Subtraction
| Division of division_t

Types can be detected by pattern matching.

match m with
| Addition (a, b) -> (* + logic *)
| Multiplication (t1, t2) -> (* * logic *)
| Subtraction ->
| Division dt -> match dt.dividend / dt.divisor with ...

Structures

The flow control instructions are quite simple.

if condition then
x
else
y

Since Liquidity is a functional language, every instruction will return a result. So you can assign the if instruction to a name.

let x_or_y = if condition then x else y in
...

Another important one is using pattern matching to control the workflow.

let result = match pattern with
| A x ->
| B x ->
| _ ->
in
...

A simple demo

type method = 
| Addition
| Multiplication
| Subtraction
| Division
let%entry main
(parameter : nat * nat * method)
(storage : nat)
: nat * nat =
let (a, b, m) = parameter in
let result = match m with
| Addition -> a + b

| Multiplication -> a * b
| Subtraction ->
(match%nat a - b with | Plus x -> x | Minus _ -> 0p)
| Division ->
match a / b with
| None -> Current.fail ()
| Some x -> x.(0)
in
(result, result)

This contract take a (nat, (nat, method)) type as the parameter and will do the calculation according to the method. Then return the result and update the storage with the last result.

So if we call this contract with (5p, (8p, Multiplication)), it will return 40p and the storage will be updated to 40p. Notice that if you are calling the contract from outside, you won’t get the result returned. Instead of this, you can check the storage of the contract to see if contract works in the right way.

How to compile it?

macOS / Linux

./liquidity-mini demo.liq

Windows

liquidity-mini.exe demo.liq

Then a file named demo.liq.tz will be generated at the same folder of the demo.liq.

---------------------------------------------------------------------------------------------- — -

We just have a glance at how to create a Tezos contract though Liquidity. According to the purpose of this series, we won’t cover all the contents. If you are interested in this, go here to learn more about Liquidity.

Donation BTC: 1L7oCqy7GHx7EiQc9SPFputCWftfaoT3kB

View the next part…Let’s deploy our first Michelson contract on Tezos.

--

--