"access function" -> "main function"
This commit is contained in:
parent
f4d688df7b
commit
feaf143209
@ -1,24 +1,24 @@
|
||||
---
|
||||
id: entrypoints-contracts
|
||||
title: Access function and Entrypoints
|
||||
title: Main function and Entrypoints
|
||||
---
|
||||
|
||||
## Access Functions
|
||||
|
||||
A LIGO contract is made of a series of constant and function
|
||||
declarations. Only functions having a special type can be called when
|
||||
the contract is activated: we called them *access functions*. An
|
||||
access function takes two parameters, the *contract parameter* and the
|
||||
the contract is activated: we called them *main functions*. A main
|
||||
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.
|
||||
|
||||
When the contract is originated, the initial value of the storage is
|
||||
provided. When an access function is later called, only the parameter
|
||||
is provided, but the type of an access function contains both.
|
||||
provided. When a main function is later called, only the parameter is
|
||||
provided, but the type of a main function contains both.
|
||||
|
||||
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
|
||||
type of an access function is as follows, assuming that the type
|
||||
type of a main 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.)
|
||||
|
||||
@ -42,13 +42,12 @@ type return = (list (operation), storage);
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
The contract storage can only be modified by activating an access
|
||||
The contract storage can only be modified by activating a main
|
||||
function. It is important to understand what that means. What it does
|
||||
*not* mean is that some global variable holding the storage is
|
||||
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.
|
||||
modified by the main function. Instead, what it *does* mean is that,
|
||||
given the state of the storage *on-chain*, a main function specifies
|
||||
how to create another state for it, depending on a parameter.
|
||||
|
||||
Here is an example where the storage is a single natural number that
|
||||
is updated by the parameter.
|
||||
@ -89,17 +88,18 @@ let main = ((action, store): (parameter, storage)) : return =>
|
||||
|
||||
## 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*.
|
||||
In LIGO, the design pattern is to have *one* main function called
|
||||
`main`, 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
|
||||
the unique main 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
|
||||
are called. In other terms, the unique main function dispatches the
|
||||
control flow depending on a *pattern matching* on the contract
|
||||
parameter.
|
||||
|
||||
@ -128,7 +128,7 @@ function entry_A (const n : nat; const store : storage) : return is
|
||||
function entry_B (const s : string; const store : storage) : return is
|
||||
((nil : list (operation)), store with record [name = s])
|
||||
|
||||
function access (const action : parameter; const store : storage): return is
|
||||
function main (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)
|
||||
@ -154,7 +154,7 @@ let entry_A (n, store : nat * storage) : return =
|
||||
let entry_B (s, store : string * storage) : return =
|
||||
([] : operation list), {store with name = s}
|
||||
|
||||
let access (action, store: parameter * storage) : return =
|
||||
let main (action, store: parameter * storage) : return =
|
||||
match action with
|
||||
Action_A n -> entry_A (n, store)
|
||||
| Action_B s -> entry_B (s, store)
|
||||
@ -179,7 +179,7 @@ let entry_A = ((n, store): (nat, storage)) : return =>
|
||||
let entry_B = ((s, store): (string, storage)) : return =>
|
||||
(([] : list (operation)), {...store, name : s});
|
||||
|
||||
let access = ((action, store): (parameter, storage)) : return =>
|
||||
let main = ((action, store): (parameter, storage)) : return =>
|
||||
switch (action) {
|
||||
| Action_A (n) => entry_A ((n, store))
|
||||
| Action_B (s) => entry_B ((s, store))
|
||||
@ -249,7 +249,7 @@ This example shows how `sender` or `source` can be used to deny access to an ent
|
||||
```pascaligo group=c
|
||||
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||
|
||||
function filter (const action : parameter; const store : storage) : return is
|
||||
function main (const action : parameter; const store : storage) : return is
|
||||
if source =/= owner then (failwith ("Access denied.") : return)
|
||||
else ((nil : list(operation)), store)
|
||||
```
|
||||
@ -258,7 +258,7 @@ function filter (const action : parameter; const store : storage) : return is
|
||||
```cameligo group=c
|
||||
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
||||
|
||||
let filter (action, store: parameter * storage) : return =
|
||||
let main (action, store: parameter * storage) : return =
|
||||
if source <> owner then (failwith "Access denied." : return)
|
||||
else (([] : operation list), store)
|
||||
```
|
||||
@ -267,7 +267,7 @@ let filter (action, store: parameter * storage) : return =
|
||||
```reasonligo group=c
|
||||
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||
|
||||
let access = ((action, store): (parameter, storage)) : storage => {
|
||||
let main = ((action, store): (parameter, storage)) : storage => {
|
||||
if (source != owner) { (failwith ("Access denied.") : return); }
|
||||
else { (([] : list (operation)), store); };
|
||||
};
|
||||
|
@ -11,13 +11,14 @@ We will be implementing a counter contract.
|
||||
## Dry-running a Contract
|
||||
|
||||
Testing a contract can be quite easy if we utilize LIGO's built-in dry
|
||||
run feature. Dry-run works by simulating the access function
|
||||
execution, as if it were deployed on a real chain. You need to provide
|
||||
the following:
|
||||
run feature. Dry-run works by simulating the main function execution,
|
||||
as if it were deployed on a real chain. You need to provide the
|
||||
following:
|
||||
|
||||
- `file` - contract to run
|
||||
- `entrypoint` - name of the function to execute
|
||||
- `parameter` - parameter passed to the access function (in a theoretical invocation operation)
|
||||
- `parameter` - parameter passed to the main function (in a
|
||||
theoretical invocation operation)
|
||||
- `storage` - a mock storage value, as if it were stored on a real chain
|
||||
|
||||
Here is a full example:
|
||||
@ -33,15 +34,15 @@ ligo dry-run src/basic.ligo main Unit Unit
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
Output of the `dry-run` is the return value of our access function, we
|
||||
can see the operations emited - in our case an empty list, and the new
|
||||
storage value being returned - which in our case is still `Unit`.
|
||||
Output of the `dry-run` is the return value of our main function, we
|
||||
can see the operations emitted (in our case an empty list, and the new
|
||||
storage value being returned) which in our case is still `Unit`.
|
||||
|
||||
## A Counter Contract
|
||||
|
||||
Our counter contract will store a single `int` as it's storage, and
|
||||
will accept an `action` variant in order to re-route our single `main`
|
||||
access function to two entrypoints for `addition` and `subtraction`.
|
||||
function to two entrypoints for `addition` and `subtraction`.
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
|
@ -99,7 +99,7 @@ called the *head*, and the sub-list after the head is called the
|
||||
think of a list a *stack*, where the top is written on the left.
|
||||
|
||||
> 💡 Lists are needed when returning operations from a smart
|
||||
> contract's access function.
|
||||
> contract's main function.
|
||||
|
||||
### Defining Lists
|
||||
|
||||
|
@ -11,7 +11,7 @@ type return is list (operation) * taco_shop_storage
|
||||
const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"
|
||||
const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"
|
||||
|
||||
function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
|
||||
function main (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
|
||||
block {
|
||||
// Retrieve the taco_kind from the contract's storage or fail
|
||||
const taco_kind : taco_supply =
|
||||
|
@ -8,7 +8,7 @@ type taco_shop_storage is map (nat, taco_supply)
|
||||
|
||||
type return is list (operation) * taco_shop_storage
|
||||
|
||||
function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is
|
||||
function main (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is
|
||||
block {
|
||||
// Retrieve the taco_kind from the contract's storage or fail
|
||||
const taco_kind : taco_supply =
|
||||
|
@ -76,18 +76,18 @@ screenshot below:
|
||||
<img src="/img/tutorials/get-started/tezos-taco-shop-smart-contract/install-ligo.png" />
|
||||
<div style="opacity: 0.7; text-align: center; font-size: 12px; margin-top:-24px;">Installing the <b>next</b> version of LIGO's CLI</div>
|
||||
|
||||
## Implementing our first access function
|
||||
## Implementing our First `main` Function
|
||||
|
||||
> From now on we will get a bit more technical. If you run into
|
||||
> something we have not covered yet - please try checking out the
|
||||
> [LIGO cheat sheet](api/cheat-sheet.md) for some extra tips & tricks.
|
||||
|
||||
To begin implementing our smart contract, we need an *access
|
||||
function*, that is the first function being executed. We will call it
|
||||
`main` and it will specify our contract's storage (`int`) and input
|
||||
parameter (`int`). Of course this is not the final storage/parameter
|
||||
of our contract, but it is something to get us started and test our
|
||||
LIGO installation as well.
|
||||
To begin implementing our smart contract, we need a *main function*,
|
||||
that is the first function being executed. We will call it `main` and
|
||||
it will specify our contract's storage (`int`) and input parameter
|
||||
(`int`). Of course this is not the final storage/parameter of our
|
||||
contract, but it is something to get us started and test our LIGO
|
||||
installation as well.
|
||||
|
||||
### `taco-shop.ligo`
|
||||
```pascaligo group=a
|
||||
@ -99,7 +99,7 @@ list (operation) * int is
|
||||
Let us break down the contract above to make sure we understand each
|
||||
bit of the LIGO syntax:
|
||||
|
||||
- **`function main`** - definition of the access function, which takes
|
||||
- **`function main`** - definition of the main function, which takes
|
||||
a the parameter of the contract and the storage
|
||||
- **`(const parameter : int; const contractStorage : int)`** -
|
||||
parameters passed to the function: the first is called `parameter`
|
||||
@ -129,11 +129,11 @@ also an `int`.
|
||||
|
||||
The `dry-run` command requires a few parameters:
|
||||
- **contract** *(file path)*
|
||||
- **entrypoint** *(name of the access function in the contract)*
|
||||
- **entrypoint** *(name of the main function in the contract)*
|
||||
- **parameter** *(parameter to execute our contract with)*
|
||||
- **storage** *(starting storage before our contract's code is executed)*
|
||||
|
||||
It outputs what is returned from our access function: in our case a
|
||||
It outputs what is returned from our main function: in our case a
|
||||
tuple containing an empty list (of operations to apply) and the new
|
||||
storage value, which, in our case, is the sum of the previous storage
|
||||
and the parameter we have used for the invocation.
|
||||
@ -176,7 +176,7 @@ type taco_supply is record [
|
||||
type taco_shop_storage is map (nat, taco_supply)
|
||||
```
|
||||
|
||||
Next step is to update the `main` access function to include
|
||||
Next step is to update the `main` function to include
|
||||
`taco_shop_storage` in its storage. In the meanwhile, let us set the
|
||||
`parameter` to `unit` as well to clear things up.
|
||||
|
||||
@ -249,17 +249,15 @@ ligo dry-run taco-shop.ligo --syntax pascaligo main unit "map [
|
||||
## Providing another Access Function for Buying Tacos
|
||||
|
||||
Now that we have our stock well defined in form of storage, we can
|
||||
move on to the actual sales. We will replace the `main` access
|
||||
function with another, `buy_taco`, that takes a key `id` from our
|
||||
`taco_shop_storage` map. This will allow us to calculate pricing, and
|
||||
if the sale is successful, we will be able to reduce our stock because
|
||||
we have sold a taco!
|
||||
move on to the actual sales. The `main` function will take a key `id`
|
||||
from our `taco_shop_storage` map. This will allow us to calculate
|
||||
pricing, and if the sale is successful, we will be able to reduce our
|
||||
stock because we have sold a taco!
|
||||
|
||||
### Selling the Tacos for Free
|
||||
|
||||
Let is start by customizing our contract a bit, we will:
|
||||
|
||||
- rename the access function from `main` to `buy_taco`
|
||||
- rename `parameter` to `taco_kind_index`
|
||||
- change `taco_shop_storage` to a `var` instead of a `const`, because
|
||||
we will want to modify it
|
||||
@ -275,7 +273,7 @@ type taco_shop_storage is map (nat, taco_supply)
|
||||
|
||||
type return is list (operation) * taco_shop_storage
|
||||
|
||||
function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
|
||||
function main (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
|
||||
((nil : list (operation)), taco_shop_storage)
|
||||
```
|
||||
|
||||
@ -303,7 +301,7 @@ type taco_shop_storage is map (nat, taco_supply)
|
||||
|
||||
type return is list (operation) * taco_shop_storage
|
||||
|
||||
function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
|
||||
function main (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is
|
||||
block {
|
||||
// Retrieve the taco_kind from the contract's storage or fail
|
||||
const taco_kind : taco_supply =
|
||||
@ -352,7 +350,7 @@ type taco_shop_storage is map (nat, taco_supply)
|
||||
|
||||
type return is list (operation) * taco_shop_storage
|
||||
|
||||
function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is
|
||||
function main (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is
|
||||
block {
|
||||
// Retrieve the taco_kind from the contract's storage or fail
|
||||
const taco_kind : taco_supply =
|
||||
@ -381,7 +379,7 @@ In order to test the `amount` sent, we will use the `--amount` option
|
||||
of `dry-run`:
|
||||
|
||||
```zsh
|
||||
ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map [
|
||||
ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 main 1n "map [
|
||||
1n -> record [
|
||||
current_stock = 50n;
|
||||
max_price = 50tez
|
||||
|
Loading…
Reference in New Issue
Block a user