Attribute for entries
Motivation and Context
This MR is an up-to-date version of an older MR introducing an attribute for entries, and which could be useful until proper contracts arrive to LIGO.
Description
These changes do not change syntax, instead we introduce an attribute @entry
to mark a function as an entry inside the module/namespace, or in the top-level.
The following examples currently work in the branch.
Top-level JsLIGO
For example, the file contract.jsligo
type storage = int;
// @entry
const increment = (action: int, store: storage) : [list <operation>, storage] => [list([]), store + action];
// @entry
const decrement = (action: int, store: storage) : [list <operation>, storage] => [list([]), store - action];
// @entry
const reset = (_u: unit, _store: storage) : [list<operation>, storage] => [list([]), 0];
can be compiled directly:
$ ligo compile contract contract.jsligo
{ parameter (or (or (int %decrement) (int %increment)) (unit %reset)) ;
storage int ;
code { UNPAIR ;
IF_LEFT { IF_LEFT { SWAP ; SUB } { ADD } } { DROP 2 ; PUSH int 0 } ;
NIL operation ;
PAIR } }
Namespace contract JsLIGO
$ cat src/test/contracts/interpreter_tests/test_originate_module.jsligo
namespace C {
type storage = int;
// @entry
const increment = (action: int, store: storage) : [list <operation>, storage] => [list([]), store + action];
// @entry
const decrement = (action: int, store: storage) : [list <operation>, storage] => [list([]), store - action];
};
const test_increment = (() => {
let initial_storage = 42;
let [taddr, _, _] = Test.originate_module(contract_of(C), initial_storage, 0 as tez);
let contr = Test.to_contract(taddr);
let _ = Test.transfer_to_contract_exn(contr, (Increment (1)), 1 as mutez);
return assert(Test.get_storage(taddr) == initial_storage + 1);
}) ();
$ ligo run test src/test/contracts/interpreter_tests/test_originate_module.jsligo
Everything at the top-level was executed.
- test_increment exited with value ().
Module contract CameLIGO
$ cat src/test/contracts/interpreter_tests/test_originate_module.mligo
let () = Test.unset_print_values ()
module Bar = struct
module Foo = struct
[@entry] let add n s : operation list * int = [], s + n
[@entry] let sub n s : operation list * int = [], s - n
[@view] let get () s : int = s
[@view] let get_diff k s : int = s - k
end
end
let test =
let ta, m, _ = Test.originate_module (contract_of Bar.Foo) 0 0tez in
let () = Test.println "Deployed the contract:" in
let () = Test.println (Test.to_string m) in
let () = Test.println ("With storage: " ^ Test.to_string (Test.get_storage ta)) in
let _ = Test.transfer_to_contract_exn (Test.to_contract ta) (Add 42) 0tez in
let () = Test.println ("Storage after call: " ^ Test.to_string (Test.get_storage ta)) in
()
$ ligo run test src/test/contracts/interpreter_tests/test_originate_module.mligo
Deployed the contract:
{ parameter (or (int %add) (int %sub)) ;
storage int ;
code { UNPAIR ; IF_LEFT { ADD } { SWAP ; SUB } ; NIL operation ; PAIR } ;
view "get" unit int { CDR } ;
view "get_diff" int int { UNPAIR ; SWAP ; SUB } }
With storage: 0
Storage after call: 42
Note the usage of contract_of(M)
as a helper for Test.originate_module
.
Component
-
compiler -
website -
webide -
vscode-plugin -
debugger
Types of changes
-
Bug fix (non-breaking change which fixes an issue) -
New feature (non-breaking change which adds functionality) -
Breaking change (fix or feature that would cause existing functionality to not work as expected) -
Performance improvement (non-breaking change that improves performance) -
None (change with no changelog)
Changelog
New attribute @entry
allows to define entries for a module/namespace, or from the top-level of a file.
For example, the file contract.jsligo
type storage = int;
// @entry
const increment = (action: int, store: storage) : [list <operation>, storage] => [list([]), store + action];
// @entry
const decrement = (action: int, store: storage) : [list <operation>, storage] => [list([]), store - action];
// @entry
const reset = (_u: unit, _store: storage) : [list<operation>, storage] => [list([]), 0];
can be compiled directly:
$ ligo compile contract contract.jsligo
{ parameter (or (or (int %decrement) (int %increment)) (unit %reset)) ;
storage int ;
code { UNPAIR ;
IF_LEFT { IF_LEFT { SWAP ; SUB } { ADD } } { DROP 2 ; PUSH int 0 } ;
NIL operation ;
PAIR } }
Moreover, when using the testing framework, a contract_of
keyword can be used to originate a module as a contract with the help of the new function Test.originate_module
:
$ cat src/test/contracts/interpreter_tests/test_originate_module.jsligo
namespace C {
type storage = int;
// @entry
const increment = (action: int, store: storage) : [list <operation>, storage] => [list([]), store + action];
// @entry
const decrement = (action: int, store: storage) : [list <operation>, storage] => [list([]), store - action];
};
const test_increment = (() => {
let initial_storage = 42;
let [taddr, _, _] = Test.originate_module(contract_of(C), initial_storage, 0 as tez);
let contr = Test.to_contract(taddr);
let _ = Test.transfer_to_contract_exn(contr, (Increment (1)), 1 as mutez);
return assert(Test.get_storage(taddr) == initial_storage + 1);
}) ();
$ ligo run test src/test/contracts/interpreter_tests/test_originate_module.jsligo
Everything at the top-level was executed.
- test_increment exited with value ().
Breaking changes
Notice that this feature is breaking as contract_of
becomes a keyword, rending invalid contracts that use this name as a variable. In this case, the fix is simple, and just amounts to change the name of the variable, or use @contract_of
:
$ cat contract.jsligo
// @entry
const increment = (action: int, store: int) : [list <operation>, int] => [list([]), store + action];
const contract_of = 42;
$ ligo compile contract contract.jsligo
File "contract.jsligo", line 4, characters 6-17:
3 |
4 | const contract_of = 42;
Ill-formed value declaration.
At this point, a pattern is expected, e.g. a variable.
After modifying contract.jsligo
with @contract_of
:
$ cat contract.jsligo
// @entry
const increment = (action: int, store: int) : [list <operation>, int] => [list([]), store + action];
const @contract_of = 42;
$ ligo compile contract contract.jsligo
{ parameter int ;
storage int ;
code { UNPAIR ; ADD ; NIL operation ; PAIR } }
This MR also modifies the parameters for the sub-command evaluate-call
(-e
is not used anymore to signal the function to compile).
Checklist:
-
Changes follow the existing coding style (use dune @fmt
to check). -
Tests for the changes have been added (for bug fixes / feature). -
Documentation has been updated. -
Changelog description has been added (if appropriate). -
Start titles under ## Changelog
section with #### (if appropriate). -
There is no image or uploaded file in changelog -
Examples in changed behaviour have been added to the changelog (for breaking change / feature).