Skip to main content

Entrypoint

The contract code is structured as a set of entrypoints. The execution of an entrypoint may change the contract storage and/or generate blockchain operations (a transfer of tez to a contract or account, or a call to another contract's entrypoint).

Entry

An entrypoint is declared with the entry keyword followed by the entrypoint identifier and the list of parameters; a parameter is defined by an identifier and a type (basic or composite).

For example:

entry %transfer(%from : address, %to: adress, amount : nat) {
/* ... body ... */
}

The body of an entrypoint is composed of sections presented below.

Transition

A transition is an entrypoint that changes the state of the contract desgined as a state machine.

The transition keyword declares a transition, followed by the entrypoint identifier and the list of parameters.

The body of the transaction is made of the following sections:

from

The from section, followed by a state value, specifies the required state of the contract to transition from. It fails with "INVALID_STATE" if the transition is called while the contract is in another state.

For example:

transition accept() {
from Shipped
/* ... */
}

to

The to section, followed by a state value, specifies the state to transition to. It is optionally followed by:

  • when and a boolean expression to specify a guard condition
  • with effect to specify an effect (on storage and/or operations)

For example, the following entrypoint transitions from Shipped to Accepted:

transition accept() {
from Shipped
to Accepted
}

A transition may have an effect, like for example transfering the balance of the contract to the seller address:

transition accept() {
from Shipped
to Accepted
with effect {
transfer balance to seller
}
}

It is possible to transition to different states according to guard conditions; it transitions to the first state whose guard condition is verified.

For example, the following transitions from Shipped state to Accepted when success is true, and to Canceled otherwise:

transition accept(success : bool) {
from Shipped
to Accepted when { success } with effect {
transfer balance to seller
}
to Canceled /* when success is false */
}

Any section (except state is) may also be added to the body of a transaction.

For example the following transition may only be called by the transporter address and it sets the storage variable message value:

transition accept(success : bool, msg : string) {
called by transporter
from Shipped
to Accepted when { success } with effect {
transfer balance to seller
}
to Canceled /* when success is false */
effect {
message := msg
}
}

Getter

A getter is the entrypoint type dedicated to the TZIP-4 view pattern: it takes an argument, and "returns" a value; the return mechanism is implemented in the form of an argument callback whose argument is the returned value, and called by the getter.

The getter syntax hides the callback argument and uses the return keyword to specify the value to pass to the callback.

For example, the following declares a storage variable bar and the getBar getter that returns this value:

variable bar : nat = 0
variable msg : string = ""

getter getBar(s : string) : nat {
msg := s;
return (bar + length(s))
}

Note that the returned value's type (here nat)is specified after the list of arguments (after a semicolon).

This is syntactic sugar for the following equivalent code:

variable bar : nat = 0
variable msg : string = ""

entry getBar(s : string, callback : contract<nat>) {
msg := s;
transfer transferred to entry callback(bar + length(s))
}

(see transfer instruction for more information)

The following illustrates how the getBar getter would be called:

variable foo : nat = 0

entry setFoo(v : nat) {
foo := v
}

entry getFoo(getter_addr : address) {
transfer 0tz to getter_addr call getBar<contract<nat>>(self.setFoo)
}

The getFoo entrypoint calls the getBar entry of contract at address ca.

info

The TZIP-4 standard is officially deprecated as it is replaced by the view feature (available since Ithaca protocol).

The benefit of the view feature is that it does not require to split the execution flow in two entries (as in getFoo and setFoo example above). Hence a view is to be preferred whenever the getter's contract storage is not modified.

Sections

The body of an entrypoint ('entry', 'transition', 'getter') is made of the following sections. Each section is optional and appears in the order of presentation below.

no transfer

Fails with "NO_TRANSFER" if the value of transferred is different from 0tz

A specific error message can be specified with the otherwise keyword:

entry exec() {
no transfer otherwise "NO_FUND_EXPECTED"
/* ... */
}

sourced by

Fails with "INVALID_SOURCE" if the value of source is different from the argument address.

For example, the set_owner_candidate entry point fails if not called by owner address:

entry set_owner_candidate(oc : address) {
sourced by owner
/* ... other sections ... */
}

See called by section below for more information.

A specific error message can be specified with the otherwise keyword.


called by

Fails with "INVALID_CALLER" if the value of caller is different from the argument address.

For example, the set_owner_candidate entry point fails if not called by owner address:

entry set_owner_candidate(oc : address) {
called by owner
/* ... other sections ... */
}

The argument of the section may also be an asset identified by an address typed field.

(this also applies to source by section above)

For example:

entry set_owner_candidate(oc : address) {
called by owner or admin otherwise "EXPECTS_OWNER_OR_ADMIN"
/* ... other sections ... */
}

The vote entry below fails if caller is not a voter (that is if voter.contains(caller) evaluates to false):

asset voter {
id : address;
nb_votes : nat = 0;
}

entry vote(proposal : nat) {
called by voter
/* ... other sections ... */
}

state is

Fails with "INVALID_STATE" if the value of state is different from the argument state.

For example, the redeem entry point below fails if the contract's state is not Canceled:

entry redeem() {
state is Canceled
/* ... other sections ... */
}

A specific error message can be specified with the otherwise keyword.


constant

Declaration section of local constants to be used in following sections.

A constant is declared by an identifier followed by keyword is and value. Declarations are separated by ;.

For example:

entry consume(data: bytes) {
called by consumer
constant {
hashed_data is blake2b(data);
value is get_value(hashed_data);
}
/* ... */
}

hashed_data and value are now declared and available in following sections (require, effect, ...). As constants, their value cannot be modified.

It is possible to extract the some value of an option, and fail if it is none.

For example, say the get_value function returns an option of int; the following declares a constant named value and typed int, and fails with "NOT_FOUND" if get_value returns none:

entry consume(data: bytes) {
called by consumer
constant {
hashed_data is blake2b(data);
value ?is get_value(hashed_data) : "NOT_FOUND";
}
/* ... */
}

require

Fails if at least one of the requirements is not true. A requirement is defined by a unique identifier, a bool typed expression and an optional error value (introduced by the otherwise keyword).

For example, the pay entry point fails with:

  • (Pair "r1" "INVALID_CONDITION") when transferred is not high enough
  • "PAYMENT_PERIOD_IS_OVER" when now is beyond deadline date
entry pay() {
require {
r1: transferred > amount;
r2: now < deadline otherwise "PAYMENT_PERIOD_IS_OVER"
}
/* ... other sections ... */
}

fail if

Fails if at least one of the failing conditions is true. A failing condition is defined by a unique identifier, a bool typed expression and an optional message value (introduced by the with keyword).

For example, the code below is equivalent to the code in the above section:

entry pay() {
fail if {
f1: transferred <= amount;
f2: now >= deadline with "PAYMENT_PERIOD_IS_OVER"
}
/* ... other sections ... */
}

effect

This section contains the code (a list of instructions) to modify the contract's storage and to generate operations.

For example, the entry point below sets the storage variable owner_candidate:

variable owner_candidate : option<address> = none

entry set_owner_candidate(oc : address) {
called by owner
effect {
owner_candidate := some(oc)
}
}

If the effect section is the only section in entry point body, the effect keyword may be omitted and the code put straightforwardly in top brackets; for example the above entry may be written as:

entry set_owner_candidate(oc : address) {
if caller <> owner then fail("INVALID_CALLER");
owner_candidate := some(oc)
}