ligo/gitlab-pages/docs/advanced/entrypoints-contracts.md

413 lines
11 KiB
Markdown
Raw Normal View History

---
id: entrypoints-contracts
2020-02-10 22:07:20 +04:00
title: Access function and Entrypoints
---
2020-02-10 22:07:20 +04:00
## Access Functions
2020-02-05 19:28:40 +04:00
A LIGO contract is made of a series of constant and function
declarations. Only functions having a special type can be called when
2020-02-10 22:07:20 +04:00
the contract is activated: we called them *access functions*. An
access function takes two parameters, the *contract parameter* and the
*on-chain storage*, and returns a pair made of a *list of operations*
and a (new) storage.
2020-02-05 19:28:40 +04:00
When the contract is originated, the initial value of the storage is
2020-02-10 22:07:20 +04:00
provided. When an access function is later called, only the parameter
is provided, but the type of an access function contains both.
2020-02-05 19:28:40 +04:00
The type of the contract parameter and the storage are up to the
contract designer, but the type for list operations is not. The return
2020-02-12 01:29:12 +04:00
type of an access function is as follows, assuming that the type
`storage` has been defined elsewhere. (Note that you can use any type
with any name for the storage.)
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
```pascaligo skip
type storage is ... // Any name, any type
type return is list (operation) * storage
```
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
```cameligo skip
type storage = ... // Any name, any type
type return = operation list * storage
```
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
```reasonligo skip
type storage = ...; // Any name, any type
type return = (list (operation), storage);
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
The contract storage can only be modified by activating an access
function. It is important to understand what that means. What it does
*not* mean is that some global variable holding the storage is
2020-02-12 01:29:12 +04:00
modified by the access function. Instead, what it *does* mean is that,
given the state of the storage *on-chain*, an access function
specifies how to create another state for it, depending on a
parameter.
2020-02-05 19:28:40 +04:00
Here is an example where the storage is a single natural number that
is updated by the parameter.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
2019-12-26 17:21:05 +04:00
```pascaligo group=a
2020-02-12 01:29:12 +04:00
type parameter is nat
2020-02-05 19:28:40 +04:00
type storage is nat
type return is list (operation) * storage
2020-02-12 01:29:12 +04:00
function save (const action : parameter; const store : storage) : return is
((nil : list (operation)), store)
```
<!--CameLIGO-->
```cameligo group=a
2020-02-12 01:29:12 +04:00
type parameter = nat
2020-02-05 19:28:40 +04:00
type storage = nat
2020-02-12 01:29:12 +04:00
type return = operation list * storage
2020-02-05 19:28:40 +04:00
2020-02-12 01:29:12 +04:00
let save (action, store: parameter * storage) : return =
(([] : operation list), store)
```
<!--ReasonLIGO-->
```reasonligo group=a
2020-02-12 01:29:12 +04:00
type parameter = nat;
2020-02-05 19:28:40 +04:00
type storage = nat;
2020-02-12 01:29:12 +04:00
type return = (list (operation), storage);
2020-02-05 19:28:40 +04:00
2020-02-12 01:29:12 +04:00
let main = ((action, store): (parameter, storage)) : return =>
(([] : list (operation)), store);
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
## Entrypoints
In LIGO, the design pattern is to have *one* access function that
dispatches the control flow according to its parameter. Those
functions called for those actions are called *entrypoints*.
As an analogy, in the C programming language, the `main` function is
the unique access function and any function called from it would be an
entrypoint.
The parameter of the contract is then a variant type, and, depending
on the constructors of that type, different functions in the contract
are called. In other terms, the unique access function dispatches the
control flow depending on a *pattern matching* on the contract
parameter.
2020-02-10 22:07:20 +04:00
In the following example, the storage contains a counter of type `nat`
and a name of type `string`. Depending on the parameter of the
contract, either the counter or the name is updated.
2020-01-28 20:00:36 +04:00
<!--DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
```pascaligo group=b
type parameter is
2020-02-12 01:29:12 +04:00
Action_A of nat
| Action_B of string
2020-02-05 19:28:40 +04:00
type storage is record [
counter : nat;
name : string
]
type return is list (operation) * storage
2020-02-12 01:29:12 +04:00
function entry_A (const n : nat; const store : storage) : return is
2020-02-05 19:28:40 +04:00
((nil : list (operation)), store with record [counter = n])
2020-02-12 01:29:12 +04:00
function entry_B (const s : string; const store : storage) : return is
2020-02-05 19:28:40 +04:00
((nil : list (operation)), store with record [name = s])
2020-02-12 01:29:12 +04:00
function access (const action : parameter; const store : storage): return is
case action of
Action_A (n) -> entry_A (n, store)
| Action_B (s) -> entry_B (s, store)
2020-02-05 19:28:40 +04:00
end
2020-01-28 20:00:36 +04:00
```
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
```cameligo group=b
type parameter =
2020-02-12 01:29:12 +04:00
Action_A of nat
| Action_B of string
2020-02-05 19:28:40 +04:00
type storage = {
counter : nat;
name : string
}
type return = operation list * storage
2020-02-12 01:29:12 +04:00
let entry_A (n, store : nat * storage) : return =
2020-02-05 19:28:40 +04:00
([] : operation list), {store with counter = n}
2020-02-12 01:29:12 +04:00
let entry_B (s, store : string * storage) : return =
2020-02-05 19:28:40 +04:00
([] : operation list), {store with name = s}
2020-02-12 01:29:12 +04:00
let access (action, store: parameter * storage) : return =
match action with
Action_A n -> entry_A (n, store)
| Action_B s -> entry_B (s, store)
2020-01-28 20:00:36 +04:00
```
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
```reasonligo group=b
type parameter =
2020-02-12 01:29:12 +04:00
| Action_A (nat)
| Action_B (string);
2020-02-05 19:28:40 +04:00
type storage = {
counter : nat,
name : string
};
type return = (list (operation), storage);
2020-02-12 01:29:12 +04:00
let entry_A = ((n, store): (nat, storage)) : return =>
(([] : list (operation)), {...store, counter : n});
2020-02-05 19:28:40 +04:00
2020-02-12 01:29:12 +04:00
let entry_B = ((s, store): (string, storage)) : return =>
(([] : list (operation)), {...store, name : s});
2020-02-05 19:28:40 +04:00
2020-02-12 01:29:12 +04:00
let access = ((action, store): (parameter, storage)) : return =>
switch (action) {
| Action_A (n) => entry_A ((n, store))
| Action_B (s) => entry_B ((s, store))
};
2020-01-28 20:00:36 +04:00
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
## Tezos-specific Built-ins
2020-02-05 19:28:40 +04:00
A LIGO smart contract can query part of the state of the Tezos
blockchain by means of built-in values. In this section you will find
how those built-ins can be utilized.
2020-02-05 19:28:40 +04:00
### Accepting or Declining Tokens in a Smart Contract
2020-02-05 19:28:40 +04:00
This example shows how `amount` and `failwith` can be used to decline
any transaction that sends more tez than `0mutez`, that is, no
incoming tokens are accepted.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-02-05 19:28:40 +04:00
```pascaligo group=c
type parameter is unit
type storage is unit
type return is list (operation) * storage
2020-02-12 01:29:12 +04:00
function deny (const action : parameter; const store : storage) : return is
2020-02-05 19:28:40 +04:00
if amount > 0mutez then
(failwith ("This contract does not accept tokens.") : return)
else ((nil : list (operation)), store)
```
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
```cameligo group=c
type parameter = unit
type storage = unit
type return = operation list * storage
2020-02-12 01:29:12 +04:00
let deny (action, store : parameter * storage) : return =
2020-02-05 19:28:40 +04:00
if amount > 0mutez then
(failwith "This contract does not accept tokens.": return)
else (([] : operation list), store)
```
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
```reasonligo group=c
type parameter = unit;
type storage = unit;
type return = (list (operation), storage);
2020-02-12 01:29:12 +04:00
let deny = ((action, store): (parameter, storage)) : return => {
if (amount > 0mutez) {
2020-02-05 19:28:40 +04:00
(failwith("This contract does not accept tokens."): return); }
else { (([] : list (operation)), store); };
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
### Access Control
This example shows how `sender` or `source` can be used to deny access to an entrypoint.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2019-12-26 17:21:05 +04:00
```pascaligo group=c
2020-02-05 19:28:40 +04:00
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
2020-02-12 01:29:12 +04:00
function filter (const action : parameter; const store : storage) : return is
2020-02-05 19:28:40 +04:00
if source =/= owner then (failwith ("Access denied.") : return)
else ((nil : list(operation)), store)
```
<!--CameLIGO-->
```cameligo group=c
2020-02-05 19:28:40 +04:00
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
2020-02-12 01:29:12 +04:00
let filter (action, store: parameter * storage) : return =
2020-02-05 19:28:40 +04:00
if source <> owner then (failwith "Access denied." : return)
else (([] : operation list), store)
```
<!--ReasonLIGO-->
```reasonligo group=c
2020-02-05 19:28:40 +04:00
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
2020-02-12 01:29:12 +04:00
let access = ((action, store): (parameter, storage)) : storage => {
2020-02-05 19:28:40 +04:00
if (source != owner) { (failwith ("Access denied.") : return); }
else { (([] : list (operation)), store); };
};
```
<!--END_DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
### Inter-Contract Invocations
2020-02-05 19:28:40 +04:00
It would be somewhat misleading to speak of "contract calls", as this
wording may wrongly suggest an analogy between contract "calls" and
function "calls". Indeed, the control flow returns to the site of a
function call, and composed function calls therefore are *stacked*,
that is, they follow a last in, first out ordering. This is not what
happens when a contract invokes another: the invocation is *queued*,
that is, follows a first in, first our ordering, and the dequeuing
only starts at the normal end of a contract (no failure). That is why
we speak of "contract invocations" instead of "calls".
2020-02-05 19:28:40 +04:00
The following example shows how a contract can invoke another by
emiting a transaction operation at the end of an entrypoint.
2020-02-05 19:28:40 +04:00
> The same technique can be used to transfer tokens to an implicit
> account (tz1, ...): all you have to do is use a unit value as the
> parameter of the smart contract.
2020-02-12 01:29:12 +04:00
In our case, we have a `counter.ligo` contract that accepts an action
of type `parameter`, and we have a `proxy.ligo` contract that accepts
the same parameter type, and forwards the call to the deployed counter
contract.
<!--DOCUSAURUS_CODE_TABS-->
2020-02-05 19:28:40 +04:00
2020-02-10 22:07:20 +04:00
<!--PascaLIGO-->
2020-01-29 20:15:03 +04:00
```pascaligo skip
// counter.ligo
2020-02-05 19:28:40 +04:00
type parameter is
Increment of nat
| Decrement of nat
| Reset
2020-02-05 19:28:40 +04:00
type storage is unit
type return is list (operation) * storage
```
2020-02-05 19:28:40 +04:00
```pascaligo group=d
// proxy.ligo
2020-02-05 19:28:40 +04:00
type parameter is
Increment of nat
| Decrement of nat
| Reset
type storage is unit
type return is list (operation) * storage
const dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
2020-02-12 01:29:12 +04:00
function proxy (const action : parameter; const store : storage): return is
2020-02-05 19:28:40 +04:00
block {
const counter : contract (parameter) = get_contract (dest);
(* Reuse the parameter in the subsequent
transaction or use another one, `mock_param`. *)
const mock_param : parameter = Increment (5n);
2020-02-12 01:29:12 +04:00
const op : operation = transaction (action, 0mutez, counter);
2020-02-05 19:28:40 +04:00
const ops : list (operation) = list [op]
} with (ops, store)
```
2020-02-05 19:28:40 +04:00
<!--CameLIGO-->
2020-02-05 19:28:40 +04:00
```cameligo skip
// counter.mligo
2020-02-05 19:28:40 +04:00
2020-02-12 01:29:12 +04:00
type parameter =
2020-02-05 19:28:40 +04:00
Increment of nat
| Decrement of nat
| Reset
// ...
```
2020-02-05 19:28:40 +04:00
```cameligo group=d
// proxy.mligo
2020-02-05 19:28:40 +04:00
type parameter =
Increment of nat
| Decrement of nat
| Reset
type storage = unit
2020-02-05 19:28:40 +04:00
type return = operation list * storage
2020-02-05 19:28:40 +04:00
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
2020-02-12 01:29:12 +04:00
let proxy (action, store : parameter * storage) : return =
2020-02-05 19:28:40 +04:00
let counter : parameter contract = Operation.get_contract dest in
(* Reuse the parameter in the subsequent
transaction or use another one, `mock_param`. *)
let mock_param : parameter = Increment (5n) in
2020-02-12 01:29:12 +04:00
let op : operation = Operation.transaction action 0mutez counter
2020-02-05 19:28:40 +04:00
in [op], store
```
<!--ReasonLIGO-->
2020-02-05 19:28:40 +04:00
```reasonligo skip
// counter.religo
2020-02-05 19:28:40 +04:00
type parameter =
| Increment (nat)
| Decrement (nat)
| Reset
// ...
```
2020-02-05 19:28:40 +04:00
```reasonligo group=d
// proxy.religo
2020-02-05 19:28:40 +04:00
type parameter =
| Increment (nat)
| Decrement (nat)
| Reset;
type storage = unit;
type return = (list (operation), storage);
2020-02-05 19:28:40 +04:00
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address);
2020-02-12 01:29:12 +04:00
let proxy = ((action, store): (parameter, storage)) : return => {
2020-02-05 19:28:40 +04:00
let counter : contract (parameter) = Operation.get_contract (dest);
(* Reuse the parameter in the subsequent
transaction or use another one, `mock_param`. *)
let mock_param : parameter = Increment (5n);
2020-02-12 01:29:12 +04:00
let op : operation = Operation.transaction (action, 0mutez, counter);
2020-02-06 14:47:41 +04:00
([op], store)
};
```
<!--END_DOCUSAURUS_CODE_TABS-->