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
hello@works-hub.com

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

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!

© 2024 WorksHub

Privacy PolicyDeveloped by WorksHub