Version: 0.23.0

Interop

LIGO can work together with other smart contract languages on Tezos. However data structures might have different representations in Michelson and not correctly match the standard LIGO types.

Michelson types and annotations#

Michelson types consist of or's and pair's, combined with field annotations. Field annotations add constraints on a Michelson type, for example a pair of (pair (int %foo) (string %bar)) will only work with the exact equivalence or the same type without the field annotations.

To clarify:

(pair (int %foo) (string %bar))

works with

(pair (int %foo) (string %bar))

works with

(pair int string)

works not with

(pair (int %bar) (string %foo))

works not with

(pair (string %bar) (int %foo))
info

In the case of annotated entrypoints - the annotated or tree directly under parameter in a contract - you should use annotations, as otherwise it's unclear which entrypoint you are referring to.

Default LIGO output#

By default LIGO translates its datatypes into a alphabetically left balanced tree. So, for example:

type animal =
| ["Elephant"]
| ["Dog"]
| ["Cat"];

will translate to:

(or
(or
(unit %cat)
(unit %dog)
)
(unit %elephant)
)

Right combed tree output#

If you want to change the data representation in Michelson to a location retaining right combed tree, like this:

(or
(unit %elephant)
(or (unit %dog)
(unit %cat)
)
)

you can use the layout:comb attribute:

type animal =
// @layout:comb
| ["Elephant"]
| ["Dog"]
| ["Cat"];

The layout:comb attribute can also be used on record types:

type artist =
// @layout:comb
{
genre: string,
since: timestamp,
name: string
};

Different Michelson annotations#

If the Michelson annotation should be different from the LIGO representation, the annot:<string> attribute can be used. For example:

type animal =
| /* @annot:memory */ ["Elephant"]
| /* @annot:face */ ["Dog"]
| /* @annot:fish */ ["Cat"]

will result into:

(or
(or
(unit %fish)
(unit %face)
)
(unit %memory)
)

The annot:<string> attribute can also be used on record field annotations:

type artist = {
/* @annot:style */ genre: string,
/* @annot:from */ since: timestamp,
/* @annot:performer */ name: string
}

If the layout:comb and annot:<string> attributes are not adequate enough for your use case, LIGO has more advanced advanced interop features which we will we discuss next.

Advanced interop with Michelson#

To interop with existing Michelson code or for compatibility with certain development tooling, LIGO has two special interop types: michelson_or and michelson_pair. These types give the flexibility to model the exact Michelson output, including field annotations.

Take for example the following Michelson type that we want to interop with:

(or
(unit %z)
(or %other
(unit %y)
(pair %other
(string %x)
(pair %other
(int %w)
(nat %v)))))

To reproduce this type we can use the following LIGO code:

type w_and_v = michelson_pair<[int, "w", nat, "v"]>;
type x_and = michelson_pair<[string, "x", w_and_v, "other"]>;
type y_or = michelson_or<[unit, "y", x_and, "other"]>;
type z_or = michelson_or<[unit, "z", y_or, "other"]>;

If you don't want to have an annotation, you need to provide an empty string.

info

Alternatively, if annotations are not important you can also use plain tuples for pair's instead. Plain tuples don't have any annotations.

To use variables of type michelson_or you have to use M_left and M_right. M_left picks the left or case while M_right picks the right or case. For michelson_pair you need to use tuples.

let z: z_or = M_left(unit) as z_or;
let y_1: y_or = M_left(unit) as y_or;
let y: z_or = M_right(y_1) as z_or;
let x_pair: x_and = ["foo", [2, 3 as nat]];
let x_1: y_or = M_right (x_pair) as y_or;
let x: z_or = M_right (y_1) as z_or;

Manual data structure conversion#

If you want to get your hands dirty, it's also possible to do manual data structure conversion.

The following code can be used as inspiration:

type z_to_v =
["Z"]
| ["Y"]
| ["X"]
| ["W"]
| ["V"];
type w_or_v = michelson_or<[unit, "w", unit, "v"]>;
type x_or = michelson_or<[unit, "x", w_or_v, "other"]>;
type y_or = michelson_or<[unit, "y", x_or, "other"]>;
type z_or = michelson_or<[unit, "z", y_or, "other"]>;
type test = {
z: string,
y: int,
x: string,
w: bool,
v: int
};
let make_concrete_sum = (r: z_to_v): z_or =>
match(r, {
Z: () => M_left(unit) as z_or,
Y: () => M_right(M_left(unit) as y_or) as z_or,
X: () => M_right (M_right (M_left(unit) as x_or) as y_or) as z_or ,
W: () => M_right (M_right (M_right(M_left(unit) as w_or_v) as x_or) as y_or) as z_or ,
V: () => M_right (M_right (M_right(M_right(unit) as w_or_v) as x_or) as y_or) as z_or
});
let make_concrete_record = (r: test): [string, int, string, bool, int] =>
[r.z, r.y, r.x, r.w, r.v];
let make_abstract_sum = (z_or: z_or): z_to_v =>
match(z_or, {
M_left: (n: unit) => Z(),
M_right: (y_or: y_or) => {
return match(y_or, {
M_left: (n: unit) => Y(),
M_right: (x_or: x_or) => {
return match(x_or, {
M_left: (n: unit) => X(),
M_right: (w_or: w_or) => {
return match(w_or, {
M_left: (n: unit) => W(),
M_right: (n: unit) => V()
})
}
})
}
})
}
})
let make_abstract_record = (z: string, y: int, x: string, w: bool, v: int): test =>
({ z: z, y, x, w, v })

Entrypoints and annotations#

It's possible for a contract to have multiple entrypoints, which translates in LIGO to a parameter with a variant type as shown here:

type storage = int;
type parameter =
["Left", int]
| ["Right", int];
let main = ([p, x]: [parameter, storage]): [list<operation>, storage] =>
[list ([]) as list<operation>, match(p, {
Left: (i: int) => x - i,
Right: (i: int) => x + i
})];

This contract can be called by another contract, like this one:

type storage = int;
type parameter = int;
type x = | ["Left", int];
let main = ([p, s]: [parameter, storage]): [list<operation>, storage] => {
let contract: contract<x> =
match (Tezos.get_entrypoint_opt("%left", "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address) as option<contract<x>>, {
Some: ( c: contract<x>) => c,
None: () => (failwith ("contract does not match") as contract<x>)
});
return [
list([Tezos.transaction(Left(2), 2 as mutez, contract)]) as list<operation>,
s];
};

Notice how we directly use the %left entrypoint without mentioning the %right entrypoint. This is done with the help of annotations. Without annotations it wouldn't be clear what our int would be referring to.

This currently only works for or's or variant types in LIGO.

Amendment#

With the upcoming 007 amendment to Tezos this will change though, and also pair's can be ordered differently.