Entrypoints
The entrypoints of a contract represent the different ways that it can be called, similar to a method or function in many programming languages or an endpoint of an API. A contract can have any number of internal functions, but only the functions designated as entrypoints can be called by outside consumers and other contracts.
Contracts must have at least one entrypoint, and they can have as many as needed.
For example, the following contract provides four entrypoints.
The increment
and decrement
entrypoints increase or decrease a value in storage, the reset
entrypoint sets the value to 0, and the default
entrypoint increases the value by 1.
To call an entrypoint, pass the name of the entrypoint with an initial capital and the parameter.
For example, this run dry-run
command calls the increment
entrypoint in the previous contract:
The response shows an empty list of transactions to run next and the new state of the storage:
Note that even though the entrypoint name starts with a lower-case letter, the run dry-run
command uses an initial upper-case letter to call it.
Parameters
LIGO entrypoints always receive two parameters:
- A parameter passed by the caller
- The current value of the contract storage
The caller provides only the first parameter; the LIGO framework provides the current value of the contract storage.
The caller-provided parameter can be of any type, including:
- Unit, to indicate no information
- A primitive data type such as integer or string
- A complex data type such as a tuple or list
Although technically speaking the entrypoint receives only one parameter from the caller, it can behave as though it receives multiple parameters by setting a complex type as the parameter type and destructuring the parameter into multiple variables. For example, this entrypoint accepts a parameter that consists of an integer, a string, and a Boolean:
Return values
LIGO entrypoints must return a tuple that contains these values:
- A list of operations to run after the entrypoint completes, such as calls to other smart contracts or transfers of tez to accounts
- The new value of the contract storage, even if the entrypoint did not change it
Unlike functions and API endpoints, entrypoints do not return a value directly to the caller. To return data from a smart contract, you can use one of these methods:
- Use views to return data to smart contracts or off-chain applications
- Use events to return data to off-chain applications
- Include a callback parameter that sends information to another smart contract by calling one of its entrypoints
Logic
An entrypoint may run logic based on:
- The contract storage
- The parameters that senders pass
- Transaction context values such as
Tezos.get_amount
andTezos.get_sender
- The table of constants
Entrypoints cannot access information outside of Tezos, such as calling external APIs. If an entrypoint needs information from outside Tezos it must use oracles; see Oracles on docs.tezos.com and Using and trusting Oracles on opentezos.com.
Storing and sending tez
Smart contracts are a type of account and can store and send tez. By default, contracts accept any tez sent to them.
If you don't want an entrypoint to accept tez, check how much tez was included with the transaction and fail the transaction if it is more than zero, as in this example:
To send tez, create a transaction with Tezos.transaction
and return it in the list of operations at the end of the entrypoint, as in this example:
Access control
This example shows how Tezos.get_sender
can be used to deny access to an
entrypoint:
The entrypoint in the previous example uses Tezos.get_sender
instead of Tezos.get_source
to prevent a security flaw.
For more information, see the Security tutorial.
Calling other contracts
An entrypoint can create any number of calls to other entrypoints in its contract and to other contracts. However, as described in Operations on docs.tezos.com, these calls happen after the entrypoint code has completed.
To call other entrypoints or contracts, create an operation and return it in the list of operations at the end of the entrypoint, as in this example:
It's important to remember that the transactions an entrypoint creates do not run until after the entrypoint code has completed. The entrypoint cannot take advantage of any changes that these transactions make. For example, if an entrypoint creates a transaction that sends tez to another contract and then checks its balance again before the end of the entrypoint, the balance is the same as it was at the start of the entrypoint. Its balance changes only when it returns the transaction at the end of the entrypoint code and that transaction runs.
Scoping
Unlike other functions in JsLIGO, entrypoints are implicitly exported from namespaces as if they had an export
keyword.
Other contracts can refer to those entrypoints.
For example, here is a contract that re-uses entrypoints from another contract even though the entrypoints are not explicitly exported:
The default entrypoint
The name default
has a special meaning for a Tezos entrypoint.
It denotes the default entrypoint that is called unless another is specified.
Because default
is a reserved keyword in JsLIGO, if you want to create an entrypoint named default
, you must escape its name as @default
.
For more information about the default entrypoint and its internal behavior, see Implementation details: the default entrypoint on docs.tezos.com.
The main function
In earlier versions of LIGO, it was possible to write a contract that had a single main function named main
that branched according to the parameter passed to it.
This way, contracts could behave as though they had multiple entrypoints while having only a single function.
While it is still possible to define a single function called main
and mark it as the sole entry point using @entry
, this is not what most contracts should do.
This feature is deprecated. Future versions of LIGO will not allow the declaration of a single main
function. A workaround is given at the end of this section.
A common way to code a contract using a single main function is to use a variant type as its parameter and branch the code according to the parameter.
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.
To call a contract that has a single main function instead of separate entrypoints, pass the parameter value without an entrypoint name.
For example, this run dry-run
command passes the Action_A
variant to the contract in the previous example:
Workaround for the deprecation of the main
function
In most cases, adding [@entry]
for CameLIGO or @entry
for JsLIGO
before the existing main
function should suffice. However in cases
where it is not possible or desirable to convert an existing
contract_main
contract to the new @entry
format (e.g. generated
code or a code review process that forbids making changes to an
already-audited file), the deprecation can be circumvented by adding a
proxy file which declares a single entry point and calls the existing
main
function, as follows:
The contract can then be compiled using the following command:
Notice that to compile a parameter for this contract, now we need to
pass the either -e proxy
or construct a value using the Proxy
constructor: