We're planting a tree for every job application! Click here to learn more

A library module in LIGO - Smart contract language for TEZOS

dinkar ganti

7 Dec 2022

3 min read

A library module in LIGO - Smart contract language for TEZOS
  • OCaml

Tezos's core Smart Contract language is Michelson, but that is a lower-level language that is not amenable to larger-scale contract development. Ligo is a friendly smart contract for Tezos.

Here I am going to present a small contract that computes holidays. The code looks very close to OCaml, therefore should make sense to most OCaml developers.

(* For example, if we would like to setup a groundwater monitoring event after on the 
first Monday of every quarter, we would use this utility. *)

 (* 
  The contract code borrows directly from the lisp and the c++ code from the references mentioned below.

  ;; The following Lisp code is from ``Calendrical
  ;; Calculations'' by Nachum Dershowitz and Edward
  ;; M. Reingold, Software---Practice & Experience, vol. 20,
  ;; no. 9 (September, 1990), pp. 899--928 and from
  ;; ``Calendrical Calculations, II: Three Historical
  ;; Calendars'' by Edward M.  Reingold, Nachum Dershowitz,
  ;; and Stewart M. Clamen, Software---Practice & Experience,
  ;; vol. 23, no. 4 (April, 1993), pp. 383--404.
  
  ;; This code is in the public domain, but any use of it
  ;; should publicly acknowledge its source.
 *)


type month = int
type day = int
type year = int

type dayOfWeek = 
  | Sunday
  | Monday
  | Tuesday
  | Wednesday
  | Thursday
  | Friday
  | Saturday


type date = {
  month : month;
  day : day;
  year : year;
}
type storage = date
let initStorage = {month = 1; day = 1; year = 1}
let xDayOnOrBefore (d : day) (x : day) : day = d - ((d - x) mod 7)

The above code is pretty straightforward, however the focus of this article is to talk about the need for tail-recursive functions because Ligo does not support generally recursive functions.

(*

if ((((year % 4) == 0) && ((year % 100) != 0))
    || ((year % 400) == 0))
*)

let lastDayOfGregorianMonth (m : month) (y : year) : day =
  if m = 0 then 0
  else if m = 2 then
    if (((int(y mod 4)) = 0 && (int ((y mod 100)) <> 0))
          || ((int (y mod 400)) = 0)) then
      29
    else
      28
  else if m = 4 then 30
  else if m = 6 then 30
  else if m = 9 then 30
  else if m = 11 then 30
  else 31

let rec daysInPriorMonthsAcc (m : month) (y : year) (accum : day) : day =
  if m <= 0 then 
    accum
  else
    daysInPriorMonthsAcc (m - 1) y (accum + lastDayOfGregorianMonth m y)

let daysInPriorMonths (m : month) (y : year) (d : day) : day = daysInPriorMonthsAcc m y d

let cast (d : date) : int =
  (daysInPriorMonths (d.month - 1) d.year d.day)
    + 365 * (d.year - 1)
    + ((d.year - 1) / 4)
    - ((d.year - 1) / 100)
    + ((d.year - 1) / 400)

let rec getApproximateYear (y : year) (absoluteDate : int) : year =
  let tmpDate : date = {
    month = 1;
    day = 1;
    year = y + 1;
  } in
  if (absoluteDate > (cast tmpDate)) then
    getApproximateYear (y + 1) absoluteDate
  else
    y

There is some more similar code, but the general idea is to convert all recursive functions to tail-recursive functions. Some more functions are presented here

let rec getApproximateMonth (y : year) (m : month) (absoluteDate : int) : month =
  let tmpDate : date = {month = m; day = lastDayOfGregorianMonth m y; year = y} in
  if (absoluteDate > cast(tmpDate)) then
    if (m < 12) then
      getApproximateMonth y (m + 1) absoluteDate
    else
      (failwith ((absoluteDate, cast(tmpDate)) : int * int))
  else
    m

let createGregorianDate (absoluteDate : int) : date =
  let year = getApproximateYear (absoluteDate / 366) absoluteDate in
  let month = getApproximateMonth year 1 absoluteDate in
  let day = absoluteDate - (cast {month = month; day = 1; year = year}) + 1
  in
    {month = month; day = day; year = year}

let nthxday (n : day) (x : day) (m : month) (y : year) (d : day) : date =
  if (n > 0) then
      let
        tmp =
          if d = 0 then
            cast {month = m; day = 1; year = y}
          else
            cast {month = m; day = d; year = y}
      in 
        createGregorianDate (7 * (n - 1) + (xDayOnOrBefore (6 + tmp) x))
  else
    let lday = lastDayOfGregorianMonth m y in
    let tmp =
      if d = 0 then
        cast {month = m; day = lday; year = y}
      else
        cast {month = m; day = d; year = y}
    in
      createGregorianDate (7 * (n + 1) + (xDayOnOrBefore tmp x))

Now for the samples that drive these contracts

type return = operation list * storage

let laborDay(y, _ : year * storage) : return = 
  let s = (nthxday 1 1 9 y 0)
  in 
    ([], s)
let main = laborDay
let memorialDay (y, _ : year * storage) : return = ([], nthxday (-1) 1 5 y 0)

let daylightSavingsStart (y, _ : year * storage) : return = ([], nthxday 2 0 3 y 0)

Some tests

let testApproximateYear (d, _ : int * storage) : return =
  let
    y = getApproximateYear (d / 366) d
  in
    let 
      s = {month = 1; day = 1; year = y}
    in
      ([], s)

let testLastDayOfGregorianMonth (_, _ : year * storage) : (operation list * storage) =
  let 
    d = lastDayOfGregorianMonth 2 2022
  in let
    s = {month = 2; day = d; year = 2022}
  in
    ([], s)

let testCreateGregorianDate (d, _ : int * storage) : (operation list * storage) =
  let
    d2 = createGregorianDate d
  in
    ([], d2)

Notes

Ligo is a great smart contract language for Tezos.

Did you like this article?

dinkar ganti

A demo app using libreoffice scripting framework: https://www.youtube.com/watch?v=INT6duJM7X8

See other articles by dinkar

Related jobs

See all

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Related articles

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

12 Sep 2021

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

12 Sep 2021

WorksHub

CareersCompaniesSitemapFunctional WorksBlockchain WorksJavaScript WorksAI WorksGolang WorksJava WorksPython WorksRemote Works
email iconhello@works-hub.comUK flag

Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ

US flag

108 E 16th Street, New York, NY 10003

Subscribe to our newsletter

Join over 111,000 others and get access to exclusive content, job opportunities and more!

© 2023 WorksHub

Privacy PolicyDeveloped by WorksHub