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 call 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 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 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.)
The contract storage can only be modified by activating a main function: given the state of the storage on-chain, a main function specifies how to create another state for it, depending on the contract's parameter.
Here is an example where the storage is a single natural number that is updated by the parameter.
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
As an analogy, in the C programming language, the
main function is
the unique main function and any function called from it would be an
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 main function dispatches the control flow depending on a pattern matching on the contract parameter.
In the following example, the storage contains a counter of type
and a name of type
string. Depending on the parameter of the
contract, either the counter or the name is updated.
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 utilised.
Accepting or Declining Tokens in a Smart Contract
This example shows how
failwith can be used to
decline any transaction that sends more tez than
0tez, that is, no
incoming tokens are accepted.
This example shows how
Tezos.get_sender can be used to deny access to an
Note that we do not use
Tezos.get_source, but instead
Tezos.get_sender. In our tutorial about security you can read more about it.
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 out 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".
The following example shows how a contract can invoke another by emitting a transaction operation at the end of an entrypoint.
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.
In our case, we have a
counter.ligo contract that accepts an action
parameter, and we have a
proxy.ligo contract that accepts
the same parameter type, and forwards the call to the deployed counter