Love primitives

Prefix & Infix primitives

There are two kinds of primitives in the language:

  • Prefix primitives are used by putting the primitive before the arguments: prim x y z.
  • Infix primitves are used by putting the primitive between the arguments: x prim y. Infix primitives are always operators (+, -, etc.).

When the type of a primitive is specified, we extend the notation for functions like this:

  • TYPE_ARG -> TYPE_RESULT for a primitive with one argument
  • TYPE_ARG1 -> TYPE_ARG2 -> TYPE_RESULT for a primitive with two arguments
  • forall TYPES. ... -> TYPE_RESULT for a polymorphic primitive

Whereas functions can only take one argument in Liquidity/Michelson (possibly a tuple), primitives can take multiple arguments.

Comparison between values

All values are not comparable. Only two values of the following types can be compared with each other:

  • unit
  • bool
  • int
  • dun
  • string
  • bytes
  • timestamp
  • keyhash
  • address
  • 'a * 'b when 'a and 'b are comparable
  • 'a option,``’a list``, 'a set when 'a is comparable
  • ('a, 'b) map when 'a and 'b are comparable
  • {field1 : 'a; field2 : 'b; ...} when 'a, 'b, … are comparable
  • A of 'a | B of 'b ... when 'a, 'b, … are comparable

The following comparison operators are available:

  • = : equal
  • <> : not-equal
  • < : strictly less
  • <= : less or equal
  • > : strictly greater
  • >= : greater or equal

These operators have type forall 'a. 'a -> 'a -> bool, hence their type must be provided with the type application [:TYPE]

Example: =[:bool] defines the equality on booleans and has type bool -> bool -> bool.

The function compare : forall 'a. 'a -> 'a -> int is a polymorphic function returning an integer, as follows.

compare[:TYPE] x y * returns 0 if x and y are equal * returns a strictly positive integer if x > y * returns a strictly negative integer if x < y

Raising exceptions

There exist two kind of exceptions in Love: user-defined exceptions and the Failure polymorphic exception.

  • User-defined exceptions are declared in contracts with the keyword exception and a list of types. They are raised with they keywork raise, and can be catched when encapsulated in try ... with EXCEPTION ->. raise is a special primitive that takes an exception in argument and returns a value of type forall 'a, 'a.

    exception MyException of bool
    
    val test (p : bool) : int =
      try (
        if p
        then (raise (MyException true)) [:int]
        else 0
      ) with
        | MyException b -> -1
    
  • failwith : forall 'a. 'a -> (forall 'b. 'b): makes the current transaction and all its internal transactions fail. No modification is done to the context. The argument can be any value (often a string and some argument), the system will display it to explain why the transaction failed. The argument and return type must be specified as the function is polymorphic.

    val%entry default storage d (p : unit) =
          let unit_value = (failwith [:string] "The transaction failed") [:unit] in
          ([][:operation], storage)
    

Operations on tuples

  • Simple projection: t.n where n is a constant positive-or-nul int: returns the n-th element of the tuple t. Starts at 0.

    type storage = int
    
    val%entry default storage d (p : int * int) =
          let new_storage = p.1 in
          ([][:operation], new_storage)
    
  • Tuple projection: t.(n_1,n_2,...) where n_i is a constant positive-or-nul int for each i: returns the tuple with the elements corresponding to (n_1,n_2,...)

    type storage = int * int
    
    val%entry default storage d (p : int * int * bool) =
          let new_storage = p.(0,1) in
          ([][:operation], new_storage)
    
  • Tuple update: t <- (EXP_1,EXP_2,...) returns the tuple where each element i has been replaced by EXP_i. EXP_i can either be an expression or _, which does not change the i-th element of the tuple.

type storage = int * int * int

val%entry default storage d (p : unit) =
      let new_storage = storage <- (_, storage.1 + 1) in
      ([][:operation], new_storage)

Operations on numeric values

Operations on numeric values are not polymorphic, each operator define a specific numeric operation.

  • Additions
    • +   : int -> int -> int
    • ++  : nat -> nat -> nat
    • ++! : nat -> int -> int
    • +!+ : int -> nat -> int
    • +$  : dun -> dun -> dun
    • +:! : timestamp -> int -> timestamp
    • +!: : int -> timestamp -> timestamp
    • +:+ : timestamp -> nat -> timestamp
    • ++: : nat -> timestamp -> timestamp
  • Substraction
    • -   : int -> int -> int
    • -+  : nat -> nat -> int
    • -+! : nat -> int -> int
    • -!+ : int -> nat -> int
    • -$  : dun -> dun -> dun
    • -:  : timestamp -> timestamp -> int
    • -:! : timestamp -> int -> timestamp
    • -!: : int -> timestamp -> timestamp
    • -:+ : timestamp -> nat -> timestamp
    • -+: : nat -> timestamp -> timestamp
  • Multiplication
    • *   : int -> int -> int
    • *+  : nat -> nat -> nat
    • *+! : nat -> int -> int
    • *!+ : int -> nat -> int
    • *$! : dun -> int -> dun option
    • *!$ : int -> dun -> dun option
    • *$+ : dun -> nat -> dun
    • *+$ : nat -> dun -> dun
  • Division (returns None when the second argument is zero)
    • / : int -> int -> (int * int) option
    • /+ : nat -> nat -> (nat * nat) option
    • /+! : nat -> int -> (int * int) option
    • /!+ : int -> nat -> (int * int) option
    • /$ : dun -> dun -> (int * dun) option
    • /$! : dun -> int -> (dun * dun) option
    • /$+ : dun -> nat -> (dun * dun) option

NB: Arithmetic operators syntax follows a certain logic. The first characher represents the operation, followed by symbols representing the argument types.

  • ! for int
  • + for nat
  • $ for dun
  • : for timestamp

For example, +!: is the operator adding int s to timestamp s.

  • Bitwise operators
    • land | lor | lxor | lsl | lsr : int -> int -> int
    • nand | nor | nxor | nlsl | nlsr : nat -> nat -> nat
  • Boolean operators (not lazy)
    • && : boolean and
    • || : boolean or
    • |& : boolean xor
    • not : boolean negation

Translators

  • Int.of_nat : nat -> int Transforms a positive integer to a signed integer.

    val decr (x : nat) : int =
      Int.of_nat x - 1
    
  • Nat.of_int : int -> nat option Transforms an integer to a positive integer. If the integer in argument is negative, it returns None.

    val is_positive (x : int) : bool =
      match Nat.of_int x with
          None -> false
        | Some _ -> true
    
  • Address.of_keyhash : keyhash -> address Transforms a keyhash to an address

    val to_address (x : keyhash) : address =
      Address.of_keyhash x
    
  • Keyhash.of_address : address -> keyhash option Transforms an address to a keyhash. If the address in argument is a contract, it returns None.

    val is_contract (x : address) : bool =
      match Keyhash.of_address x with
          None -> true
        | Some _ -> false
    

Operations on lambdas

  • ( |> ) : 'a -> ('a -> 'b) -> 'b Applies a function to its argument.

    type storage = nat
    
    val%entry default storage d (b : unit) =
      let b = storage |> Bytes.pack [:storage] |> Bytes.unpack<:nat> in
      match b with
            None -> ([][:operation], storage)
          | Some b -> ([][:operation], b)
    

Operation on lists

Lists are immutable data structures containing values (of any type) that can only be accessed in a sequential order. Since they are immutable, all modification primitives return a new list, and the list given in argument is unmodified.

  • [] : forall 'a. 'a list The empty list.

    val int_list : int list = [] [:int]
    
    val nat_list : nat list = [] [:nat]
    
    val fun_list : (int -> int) list = [] [:int -> int]
    
  • List.cons : forall 'a. 'a -> 'a list -> 'a list Add a new element at the head of the list. The previous list becomes the tail of the new list. It is equivalent to CONS in Michelson.

    type storage = int list
    
    val%entry default storage d (p : int) =
        let new_storage = p :: storage in
        ([][:operation], new_storage)
    
  • ( :: ) : forall 'a. 'a -> 'a list -> 'a list Same as List.cons, but is an infix operator. The type 'a is automatically inferred and is not required.

    type storage = int list
    
    val%entry default storage d (p : int) =
      let new_storage = List.cons p storage in
      ([][:operation], new_storage)
    
  • ( @ ) : forall 'a. 'a list -> 'a list -> 'a list. Concatenate two lists into a single list.

    type storage = int list
    
    val%entry default storage d (p : int list) =
        ([][:operation], storage @ [:int] p)
    
  • List.rev : forall 'a. 'a list -> 'a list Returns the list in the reverse order.

    type storage = int list
    
    val%entry default storage d (p : unit) =
        ([][:operation], List.rev[:int] storage)
    
  • List.length : forall 'a. 'a list -> nat. Returns the length of the list. It is equivalent to SIZE in Michelson.

    type storage = int list
    
    val%view storage_size storage (p : unit) : nat =
        List.length[:int] storage
    
  • List.iter: forall 'a. ('a -> unit) -> 'a list -> unit. Iter the function on all the elements of a list. Since no value can be returned, it can only be used for side effects, i.e. to fail the transaction. It is equivalent to ITER in Michelson.

    exception Fail
    
    type storage = int list
    
    val%view check_storage storage (p : unit) : unit =
        List.iter[:int]
            (fun (elt : int) -> if (elt <[:int] 0) then (raise Fail) [:unit])
            storage
    
  • List.fold: forall 'elt. forall 'acc. ('elt -> 'acc -> 'acc) -> 'elt list -> 'acc ->  'acc. Iter on all elements of a list, while modifying an accumulator. It is equivalent to ITER in Michelson.

    type storage = int list
    
    val%view sum_storage storage (p : unit) : int =
        List.fold [:int] [:int]
            (fun (stor_elt : int) (acc : int) -> acc + stor_elt)
    	storage
    	0
    
  • List.map: forall 'a. forall 'b. ('a -> 'b) -> 'a list -> 'b list. Returns a list with the result of applying the function on each element of the list. It is equivalent to MAP in Michelson.

    type storage = int list
    
    val%entry default storage d (p : int) =
        let new_storage =
            List.map[:int][:int] (fun (i : int) -> i + p) storage in
        ([][:operation], new_storage)
    
  • List.map_fold: forall 'a. forall 'acc. forall 'b. ('a * 'acc -> 'b * 'acc) -> 'a list -> 'acc -> 'b list * 'acc. Returns a list with the result of applying the function on each element of the list, plus an accumulator. It is equivalent to MAP in Michelson.

    type storage = int list
    
    val%entry default storage d (p : int) =
      let (new_storage, acc) =
      List.map_fold[:int] [:int] [:int]
      (fun (elt : int) (acc : int) -> ( elt + p, elt + acc ))
      ([1; 2; 3; 4; 5] [:int]) 0 in
        ([][:operation], new_storage)
    

The Bytes module

  • Bytes.pack: forall 'a. 'a -> bytes. Serialize any data to a binary representation in a sequence of bytes. It is equivalent to PACK in Michelson.

    type storage = bytes
    
    val%entry default storage d (k : address) =
        let b = Bytes.pack [:address] k in
        ([][:operation], b)
    
  • Bytes.unpack<:TYPE>: bytes -> TYPE option. Deserialize a sequence of bytes to a value from which it was serialized. The expression should be annotated with the (option) type that it should return. It is equivalent to UNPACK in Michelson.

    type storage = address
    
    val%entry default storage d (k : bytes) =
        match Bytes.unpack<:address> k with
    	  None   -> ([][:operation], storage)
            | Some a -> ([][:operation], a)
    
  • Bytes.length : bytes -> nat. Returns the size of the sequence of bytes. It is equivalent to SIZE in Michelson.

    type storage = nat
    
    val%entry default storage d (b : bytes) =
        ([][:operation], Bytes.length b)
    
  • Bytes.concat: bytes list -> bytes. Append all the sequences of bytes of a list into a single sequence of bytes. It is equivalent to CONCAT in Michelson.

    type storage = bytes
    
    val%entry default storage d (b : bytes) =
        ([][:operation], Bytes.concat ([storage; b][:bytes]))
    
  • Bytes.slice : nat -> nat -> bytes -> bytes option. Extract a sequence of bytes within another sequence of bytes. Bytes.slice start len b extracts the bytes subsequence of b starting at index start and of length len. A return value None means that the position or length was invalid. It is equivalent to SLICE in Michelson.

    type frame = {start : nat; size : nat}
    
    type storage = bytes
    
    val%view get_sub storage (b : frame) : bytes option =
        Bytes.slice b.start b.size storage
    

Operation on strings

A string is a fixed sequence of characters. They are restricted to the printable subset of 7-bit ASCII, plus some escaped characters (\n, \t, \b, \r, \\, \").

  • String.length : string -> nat. Returns the size of the string in characters. It is equivalent to SIZE in Michelson.

    type storage = string
    
    val%view length storage (p : unit) : nat = String.length storage
    
  • String.slice : nat -> nat -> string  -> string option. String.slice start len s returns a substring of a string s at the given starting at position len with the specified length len, or None if invalid. It is equivalent to SLICE in Michelson.

    type frame = {start : nat; size : nat}
    
    type storage = string
    
    val%view get_sub storage (b : frame) : string option =
        String.slice b.start b.size storage
    
  • String.concat: string list -> string. Append all strings of a list into a single string. It is equivalent to CONCAT in Michelson.

    type storage = string
    
    val%entry default storage d (b : string) =
        ([][:operation], String.concat ([storage; b][:string]))
    
  • ^: string -> string -> string. Infix operator for concatenating two strings.

    type storage = string
    
    val%entry default storage d (k : string) =
      let new_storage = storage ^ " and then " ^ k in
      ([][:operation], new_storage)
    

The Set module

Sets are immutable data structures containing unique values (a comparable type). Since they are immutable, all modification primitives return a new updated set, and the set given in argument is unmodified.

  • Set.empty: forall 'a. 'a set. The empty set

    type storage = int set
    
    val%entry default s d (p : unit) =
      ([][:operation], Set.empty [:int])
    
  • Set.cardinal: forall 'a. 'a set -> nat. The number of elements in a set.

    type storage = int set
    
    val%view storage_size s (p : unit) : nat = Set.cardinal [:int] s
    
  • Set.add: forall 'a. 'a -> 'a set -> 'a set . Add an element to a set, if not present.

    type storage = int set
    
    val%entry add_elt s d (p : int) =
      ([][:operation], Set.add [:int] p s)
    
  • Set.remove: forall 'a. 'a -> 'a set -> 'a set . Removes an element to a set, if present.

    type storage = int set
    
    val%entry remove_elt s d (p : int) =
      ([][:operation], Set.remove [:int] p s)
    
  • Set.mem: forall 'a. 'a -> 'a set -> bool. Returns true if the element is in the set, false otherwise. It is equivalent to MEM in Michelson.

    type storage = int set
    
    val%view contains storage (p : int) : bool = Set.mem [:int] p storage
    
  • Set.iter: forall 'elt. ('elt -> unit) -> 'elt set -> unit. Apply a function on all elements of the set. Since no value can be returned, it can only be used for side effects, i.e. to fail the transaction. It is equivalent to ITER in Michelson.

    exception Fail
    
    type storage = int set
    
    val%view check_storage storage (p : unit) : unit =
        Set.iter [:int]
            (fun (elt : int) -> if (elt <[:int] 0) then (raise Fail) [:unit])
            storage
    
  • Set.fold: forall 'elt. forall 'acc. ('elt -> 'acc -> 'acc) -> 'elt set -> 'acc ->  'acc. Iter on all elements of a set, while modifying an accumulator.

    type storage = int set
    
    val%view sum_storage storage (p : int) : int =
        Set.fold[:int][:int]
            (fun (i : int) (acc : int) -> i + acc)
    	storage
    	0
    
  • Set.map: forall 'a. forall 'b. ('a -> 'b) -> 'a set -> 'b set. Returns a list with the result of applying the function on each element of the list.

    type storage = int set
    
    val%view incr_storage storage (p : int) : int set =
        Set.map[:int][:int]
            (fun (i : int) -> i + 1 )
    	storage
    
  • Set.map_fold: forall 'a. forall 'acc. forall 'b. ('a -> 'acc -> 'b * 'acc) -> 'a set -> 'acc -> 'b set * 'acc. Returns a list with the result of applying the function on each element of the list, plus an accumulator.

    type storage = int set
    
    val%view incr_storage storage (p : int) : int set * int =
        Set.map_fold[:int][:int][:int]
            (fun (acc : int) (i : int) -> i + acc, i + 1 )
    	storage
    	0
    

The Map module

Maps are immutable data structures containing associations between keys (a comparable type) and values (any type). Since they are immutable, all modification primitives return a new updated map, and the map given in argument is unmodified.

  • Map.empty: forall 'key. forall 'elt. ('key, 'elt) map. The empty map that binds elements of type 'key to elements of type 'elt.

    type storage = (int, bool) map
    
    val%entry restart storage d (p : unit) =
        let new_storage = Map.empty [:int] [:bool]
        in ([][:operation], new_storage)
    
  • Map.cardinal: forall 'key. forall 'elt. ('key, 'elt) map -> nat. The number of bindings registered in a map.

    type storage = (int, bool) map
    
    val%view size_storage storage (p : unit) : nat =
        Map.cardinal [:int] [:bool] storage
    
  • Map.add: forall 'key. forall 'elt. 'key -> 'elt -> ('key, 'elt) map -> ('key, 'elt) map . Binds a key to a value into a map, if the key is not present.

    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%entry add storage d (p : int) =
        let new_storage =
            if (p <[:int] 0)
            then Map.add [:int] [:sign] p Minus storage
            else Map.add [:int] [:sign] p Plus storage
        in ([][:operation], new_storage)
    
  • Map.remove:  forall 'key. forall 'elt. 'key -> 'elt -> ('key, 'elt) map -> ('key, 'elt) map . Removes a binding from a map, if present.

    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%entry remove storage d (p : int) =
        let new_storage = Map.remove [:int] [:sign] p storage
        in ([][:operation], new_storage)
    
  • Map.mem: forall 'key. forall 'map 'key -> ('key, 'elt) map -> bool. Returns true if the key belong to the map, false otherwise.

    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%view exists storage (p : int) : bool = Map.mem [:int] [:sign] p storage
    
  • Map.find: forall 'key. forall 'map 'key -> ('key, 'elt) map -> 'elt option. Returns Some elt if elt is bound to the key in argument, None [:'elt] otherwise.

    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%view get_elt storage (p : int) : sign option = Map.find [:int] [:sign] p storage
    
  • Map.iter: forall 'key. forall 'elt. ('elt -> 'key -> unit) -> ('key, 'elt) map -> unit. Applies a function on all elements of the set. Since no value can be returned, it can only be used for side effects, i.e. to fail the transaction.

    exception Fail
    
    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%view check_storage storage (forbidden_val : int) : unit =
        Map.iter [:int] [:sign]
            (fun (elt : int) (_ : sign) -> if (elt =[:int] forbidden_val) then (raise Fail) [:unit])
            storage
    
  • Map.fold: forall 'key. forall 'elt. forall 'acc. ('key -> 'elt -> 'acc -> 'acc) -> ('key, 'elt) map -> 'acc ->  'acc. Iter on all elements of a set, while modifying an accumulator.

    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%view abs_sum storage (p : unit) : int =
        Map.fold [:int] [:sign] [:int]
            (fun (elt : int) (s : sign) (acc : int)  ->
    	     match s with
    	           Plus -> acc + elt
    		 | Minus -> acc - elt
    	)
    	storage
    	0
    
  • Map.map:  forall 'key. forall 'elt. forall 'new_elt. ('key -> 'elt -> 'new_elt) -> ('key, 'elt) map -> ('key, 'new_elt) map. Returns a list with the result of applying the function on each element of the list.

    type sign = Plus | Minus
    type storage = (int, sign) map
    
    val%entry reverse storage d (p : unit) =
        let new_storage =
        Map.map [:int] [:sign] [:sign]
            (fun (elt : int) (s : sign) ->
    	     match s with
    	           Plus -> Minus
    		 | Minus -> Plus
    	)
    	storage
        in ([][:operation], new_storage)
    
  • Map.map_fold: forall 'key. forall 'elt. forall 'acc. forall 'new_elt. ('key -> 'elt -> 'acc -> 'new_elt * 'acc) -> ('key, 'elt) map -> 'acc ->  ('key, 'new_elt) map * 'acc. Returns a list with the result of applying the function on each element of the list, plus an accumulator.

    type sign = Plus | Minus
    type storage = (int, sign) map * int
    
    val%entry reverse storage d (p : unit) =
        let new_storage =
        Map.map_fold [:int] [:sign] [:int] [:sign]
            (fun (elt : int) (s : sign) (acc : int) ->
    	     match s with
    	           Plus -> Minus, acc + elt
    		 | Minus -> Plus, acc - elt
    	)
    	storage.0
    	0
        in ([][:operation], new_storage)
    

The BigMap module

Big maps are a specific kind of maps, optimized for storing. They can be updated incrementally and scale to a high number of associations, whereas standard maps will have an expensive serialization and deserialization cost. You are limited by Michelson to one big map per smart contract, that should appear as the first element of the storage. Big maps cannot be iterated.

  • BigMap.empty<:TYPE, TYPE'>: (TYPE,TYPE') bigmap. Returns the value associated with a key in the map. It is equivalent to GET in Michelson.

    type storage = (int, address) bigmap
    
    val%entry reinit s d (p : unit) =
        ([][:operation], BigMap.empty<<int, address>>)
    
  • BigMap.add: forall 'key. forall 'val. 'key -> 'val -> ('key, 'val) big_map -> ('key, 'val) bigmap

    Returns the big map in argument with the new binding ('key, 'val) if 'key is not binded.

    type storage = {last : int; addresses : (int, address) bigmap}
    
    val%entry add s d (p : address) =
        let new_storage = {
        	last = s.last + 1;
    	addresses = BigMap.add [:int] [:address] s.last p s.addresses
        } in
        ([][:operation], new_storage)
    
  • BigMap.remove: forall 'key. forall 'val. 'key -> ('key,'val) big_map -> ('key,'val) bigmap. Returns the big map without the binding 'key.

    type storage = {last : int; addresses : (int, address) bigmap}
    
    val%entry add s d (p : int) =
        let new_storage = {s with addresses = BigMap.remove [:int][:address] p s.addresses} in
        ([][:operation], new_storage)
    
  • BigMap.mem: forall 'key. forall 'val. 'key -> ('key, 'val) bigmap -> bool. Returns true if an association exists in the big map for the key, false otherwise.

    type storage = {last : int; addresses : (int, address) bigmap}
    
    val%view exists s (p : int) : bool =
        BigMap.mem [:int] [:address] p s.addresses
    
  • BigMap.find: forall 'key. forall 'val. 'key -> ('key,'val) bigmap -> 'val option. Returns Some elt if the argument is bound to elt in the big map, None otherwise.

    type storage = {last : int; addresses : (int, address) bigmap}
    
    exception Fail
    
    val%view get s (p : int) : address =
        match BigMap.find [:int] [:address] p s.addresses with
          Some a -> a
        | None -> (raise Fail) [:address]
    

The Current module

  • Current.balance: unit -> dun: returns the balance of the current contract. The balance contains the amount of dun that was sent by the current operation. It is equivalent to BALANCE in Michelson.

    val%entry default storage d (p : unit) =
          let b = Current.balance () in
          ([][:operation], storage)
    
  • Current.time: unit -> timestamp: returns the timestamp of the block in which the transaction is included. This value is chosen by the baker that is including the transaction, so it should not be used as a reliable source of alea. It is equivalent to NOW in Michelson.

    type storage = unit
    
    val%entry default storage d (p : unit) =
          let b = Current.time () in
          ([][:operation], storage)
    
  • Current.amount: unit -> dun: returns the amount of dun transferred by the current operation (standard or internal transaction). It is equivalent to AMOUNT in Michelson.

    type storage = unit
    
    val%entry default storage d (p : unit) =
          let a = Current.amount () (* dun = a *) in
          ([][:operation], storage)
    
  • Current.gas: unit -> nat: returns the amount of gas available to execute the rest of the transaction. It is equivalent to STEPS_TO_QUOTA in Michelson.

    type storage = unit
    
    val%entry default storage d (p : unit) =
          let a = Current.gas () in
          ([][:operation], storage)
    
  • Current.self: unit -> address: returns the address of the current contract.

    type storage = address
    
    val%entry default storage d (p : unit) =
      let me = Current.self () in
      ([][:operation], me)
    
  • Current.source: unit -> address: returns the address that initiated the current top-level transaction in the blockchain. It is the same one for all the transactions resulting from the top-level transaction, standard and internal. It is the address that paid the fees and storage cost, and signed the operation on the blockchain. It is equivalent to SOURCE in Michelson.

    type storage = unit
    
    val%entry default storage d (p : unit) =
          let a = Current.source () in
          ([][:operation], storage)
    
  • Current.sender: unit -> address: returns the address that initiated the current transaction. It is the same as the source for the top-level transaction, but it is the originating contract for internal operations. It is equivalent to SENDER in Michelson.

    type storage = unit
    
    val%entry default storage d (p : unit) =
          let a = Current.sender () in
          ([][:operation], storage)
    
  • Current.cycle: unit -> cycle: returns the cycle in which the transaction initiating the execution of the current smart contract has been performed.

    type storage = nat
    
    val%entry default storage d (p : unit) =
      let now = Current.cycle () in
      ([][:operation], now)
    
  • Current.level: unit -> address: returns the cycle in which the transaction initiating the execution of the current smart contract has been performed.

    type storage = nat
    
    val%entry default storage d (p : unit) =
      let now = Current.level () in
      ([][:operation], now)
    

The Contract module

  • Contract.self<:CONTRACT TYPE> -> (instance CONTRACT TYPE) option. Returns the current executing contract. If the contract does not matched the signature in argument, returns None. It is equivalent to SELF in Michelson.

    type storage = int
    
    contract type CT = sig val x : int end
    
    val x : int = 5
    
    val%entry default storage d (() : unit) =
        let c = Contract.self<:CT> () in
        match c with
              None -> ([][:operation], storage)
            | Some (contract C : CT) -> ([][:operation], C.x)
    
  • Contract.at<:CONTRACT TYPE>: address -> (instance CONTRACT TYPE) option. Returns the contract associated with the address. As for Contract.self, it must be annotated with the type of the contract.

    type storage = int
    
    contract type CT = sig val x : int end
    
    val%entry default storage d (k : address) =
        let c = Contract.at<:CT> k in
        match c with
              None -> ([][:operation], storage)
            | Some (contract C : CT) -> ([][:operation], C.x)
    
  • Contract.call: forall 'a. 'a entrypoint -> dun -> 'a -> operation. Forges an internal contract call. It is equivalent to TRANSFER_TOKENS in Michelson. The entry point name is optional (default by default).

    type storage = unit
    
    contract type CT = sig
      val%entry default : int
    end
    
    val x : int = 5
    
    val%entry default storage d (k : address) =
        let c = Contract.at<!CT> k in
        match c with
              None -> ([][:operation], storage)
    	| Some (contract C : CT) -> (
                let op = Contract.call [:int] C.default d x in
                ([op][:operation], storage)
    	)
    
  • Contract.view: forall 'a. 'a view -> 'a. Calls a view from a contract. Views does not initiate transaction when they are called, i.e. they are called in the contect of the calling contract, but with the storage of the target contract. Contract.view [:typ] view deserializes the storage, which means that any change of the storage after is not taken into account.

    type storage = int
    
    contract type CT = sig
      type storage
      val%view storage_size : unit -> int
    end
    
    val%entry default storage d (k : address) =
        let c = Contract.at <:CT> k in
        match c with
              None -> ([][:operation], storage)
    	| Some (contract C : CT) -> (
                let size = Contract.view [:unit -> int] C.storage_size () in
                ([][:operation], storage)
    	)
    
  • Contract.set_delegate: keyhash option -> operation. Forge a delegation operation for the current contract. A None argument means that the contract should have no delegate (it falls back to its manager). The delegation operation will only be executed in an internal operation if it is returned at the end of the %entry function. It is equivalent to SET_DELEGATE in Michelson.

    type storage = unit
    
    val%entry default storage d (k : keyhash) =
        let op = Contract.set_delegate (Some k) in
        ([op][:operation], storage)
    
  • Contract.address: UnitContract instance -> address . Returns the address of a contract. It is equivalent to ADDRESS in Michelson.

    type storage = unit
    
    val%entry default storage d (k : unit) =
        let c = Contract.self<<UnitContract>> () in
        let self_addr = Contract.address c in
        ([][:operation], storage)
    
  • Contract.create: forall 'a. keyhash option -> dun -> (sig type storage val%init storage : 'a end contract) ->'a -> (operation * address). Forges an operation to originate a contract with code. The contract is only created when the operation is executed, so it must be returned by the transaction. Note that the code must be specified as a contract structure (inlined or not). Contract.create manager_opt initial_amount (contract C) arg forges an an origination operation for contract C with manager manager, optional delegate delegate, initial balance initial_amount and initial storage C.init arg.

    Warning : 'a must be a built-in type or a public alias of a built-in type.

    type storage = unit
    
    contract C = struct
      type storage = A | B
      val init (() : unit) : storage = A
    end
    
    val%entry default storage d (k : keyhash) =
        let (op, addr) = Contract.create [:unit] (Some k) d (contract C) () in
        ([op][:operation], storage)
    

The Account module

  • Account.transfer: address -> dun -> operation. Forges an internal transaction to the implicit (_i.e._ default) account contract in argument.

    type storage = int
    
    val%entry default storage d (k : address) =
        let op = Account.transfer k d in
        ([op][:operation], storage)
    
  • Account.default: keyhash -> instance UnitContract. Returns the contract associated to the given keyhash with an empty signature. It is equivalent to IMPLICIT_ACCOUNT in Michelson.

    val get_contract (k : keyhash) : instance UnitContract = Account.default k
    
  • Account.manage: address -> int option -> bool -> address option -> address list -> operation Returns an operation that updates the account informations. The operation will fail if the operation creator is not the administrator of the adress in argument.

    type info = (int option * bool * address option * address list)
    
    type storage = address
    
    val%entry manage storage d (info : info) =
        let manage_op = Account.manage storage info.0 info.1 info.2 info.3 in
        match manage_op with
              Some op -> ([op][:operation], storage)
    	| None -> ([][:operation], storage)
    
  • Account.balance: address -> dun Returns the amount of dun at a given address.

    type storage = (address, dun) map
    
    val%entry default storage d (k : address) =
      let amount = Account.balance k in
      ([][:operation], Map.add [:address] [:dun] k amount storage)
    

The Crypto module

  • Crypto.blake2b: bytes -> bytes. Computes the cryptographic hash of a bytes with the cryptographic Blake2b function. It is equivalent to BLAKE2B in Michelson.

    type storage = bytes
    
    val%entry default storage d (k : bytes) =
        let blakehash = Crypto.blake2b k in
        ([][:operation], blakehash)
    
  • Crypto.sha256: bytes -> bytes. Computes the cryptographic hash of a bytes with the cryptographic Sha256 function. It is translated to SHA256 in Michelson.

    type storage = bytes
    
    val%entry default storage d (k : bytes) =
        let hash = Crypto.sha256 k in
        ([][:operation], hash)
    
  • Crypto.sha512: bytes -> bytes. Computes the cryptographic hash of a bytes with the cryptographic Sha512 function. It is equivalent to SHA512 in Michelson.

    type storage = bytes
    
    val%entry default storage d (k : bytes) =
        let hash = Crypto.sha512 k in
        ([][:operation], hash)
    
  • Crypto.hash_key: key -> keyhash. Hash a public key and encode the hash in B58check. It is equivalent to HASH_KEY in Michelson.

    type storage = keyhash
    
    val%entry default storage d (k : key) =
        let kh = Crypto.hash_key k in
        ([][:operation], kh)
    
  • Crypto.check: key -> signature -> bytes -> bool. Check that the signature corresponds to signing the (Blake2b hash of the) sequence of bytes with the public key. It is equivalent to CHECK_SIGNATURE in Michelson. Signatures generated by dune-client sign bytes ... can be checked this way.

    type data = abstract key * signature * bytes
    
    type storage = data list
    
    val%entry default storage d (k : data) =
        if Crypto.check k.0 k.1 k.2
        then ([][:operation], k :: storage)
        else ([][:operation], storage)