Skip to main content

LIGO views

Defining On-Chain Views

On-chain views can be defined using the @view attribute. For more information see here.

cameligo
type storage = string
type ret = operation list * storage

[@entry]
let main (word : string) (store : storage) : ret
= [] , store ^ " " ^ word

(* view 'view1', simply returns the storage *)
[@view] let view1 (() : unit) (s : storage) : storage
= s

(* view 'v2', returns true if the storage has a given length *)
[@view] let v2 (expected_length : nat) (s : storage) : bool
= (String.length s = expected_length)

(* view 'v3' does not use its parameters and returns a constant int *)
[@view] let v3 (() : unit) (_ : storage) : int
= 42
jsligo
type storage = string
type ret = [list<operation>, storage];

const main = (word : string, store : storage) : ret
=> [list([]) , store + " " + word]

/* view 'view1', simply returns the storage */
@view
const view1 = (_arg : unit, s : storage) : storage
=> s;

/* view 'v2', returns true if the storage has a given length */
@view
const v2 = (expected_length : nat , s : storage) : bool
=> (String.length (s) == expected_length);

/* view 'view3' does not use its parameters and returns a constant int */
@view
const view3 = (_arg : unit , _s : storage) : int
=> 42;

Calling On-Chain Views

cameligo

val call_view : string -> 'arg -> address -> 'ret option

jsligo

let call_view : string => 'arg => address => option <'ret>

The primitive Tezos.call_view will allow you to call another contract view and get its result by providing the view name; the contract address and the parameter of the view. If the address is nonexistent; the name does not match of of the contract view or the parameter type do not match, Tezos.call_view will return None.

Defining Off-Chain Views

In addition to on-chain views, LIGO views can be compiled and used as off-chain views (e.g. to be placed in metadata).

To compile an expression as a off-chain view, we can use the LIGO sub-command compile expression, passing the --function-body flag. Moreover, the --init-file argument can be passed to re-use expressions from a file.

For example, if we have the following off_chain file containing a contract C:

cameligo
module C = struct
type storage = string

[@entry] let append (a : string) (s : storage) : operation list * storage = [] , s ^ a

[@entry] let clear (_ : unit) (_ : storage) : operation list * storage = [] , ""

let v (expected_length: nat) (s: storage) : bool = (String.length s = expected_length)
end
jsligo
namespace C {
type storage = string

@entry
const append = (a: string, s: storage) : [list<operation> , storage] => [list([]), s + a];

@entry
const clear = (_p: unit, _s: storage) : [list<operation>, storage] => [list([]), ""];

export const v = (expected_length: nat, s: storage) : bool => (String.length (s) == expected_length);
}

We can compile function v from contract C as an off-chain view as follows:

cameligo

Input

❯ ligo compile expression cameligo "C.v" --init-file off_chain.mligo --function-body

Output

{ UNPAIR ; SWAP ; SIZE ; COMPARE ; EQ }
jsligo

Input

❯ ligo compile expression jsligo "C.v" --init-file off_chain.jsligo --function-body

Output

{ UNPAIR ; SWAP ; SIZE ; COMPARE ; EQ }

Notice that v is not a contract entry of C (no @entry) nor a on-chain view (no @view), it is just a function declared in the context of the contract, which can be used as an off-chain view.