Skip to content
On this page

Entrypoints โ€‹

Declaration โ€‹

An entrypoint is a method of a contract class that can be called from the outside. entrypoints need to be marked with the @sp.entrypoint decorator.

For example, the following entrypoint checks that the argument given is larger than 2:

python
@sp.entrypoint
def check_big_enough(self, params):
    sp.verify(params > 2)

See reference Store Value template for more entrypoints examples.

@sp.entrypoint can also take optional parameters such as an alternative string name, bool private, lazify, lazy_no_code, parameter_type, and check_no_incoming_transfer flags.

ParameterTypeDescription
namestringDefines the entrypoint name (By default, the function name is used as name)
privateboolDefines a entrypoint that is only available in tests
lazifyboolMarks the entrypoint as lazy (see lazy entrypoints).
lazy_no_codeboolDoesn't originate any implementation of the entrypoint (see lazy entrypoints).
check_no_incoming_transferboolVerifies that sp.amount is 0 and fails with sp.amount otherwise.
parameter_type[type]The entrypoint input type (By default, the type input gets inferred)
python
@sp.entrypoint(name = "another_name", private = True, lazify = False, lazy_no_code = False)
def check_big_enough(self, params):
    sp.verify(params > 2)

The lazify option is False by default. Its default can be changed to True by specifying the "lazy-entrypoints" flag. For an example of this see the Send back template.

Dummy entrypoints โ€‹

It is possible to create entrypoints for testing that only appear in SmartPy but are not included in the Michelson contract.

They help to implement checks in SmartPy tests after the contract origination. One can use the entire SmartPy machinery such as sp.verify, etc.

See reference Private Entrypoint template.

This is also useful to build custom UI through simulation by doing:

python
@sp.entrypoint(private = True)
def set_y(self, params):
    self.data.y = param

Return values โ€‹

Entrypoints do not return values in Michelson.

SmartPy respects this constraint but allows other functions to return values. These functions typically use sp.result(value) to return values.

See Lambdas for examples on how to use @sp.private_lambda().

Lazy entrypoints โ€‹

An entrypoint marked as lazy (see sp.entrypoint) has two characteristics:

  • Its code is not loaded when other entrypoints are invoked. This reduces gas usage.
  • It can be updated after the contract is originated.

The entrypoint map โ€‹

A contract with at least one lazy entrypoint has an entrypoint map, accessible as sp.entrypoint_map(). It is of type sp.TBigMap(sp.TNat, sp.TLambda(...)). For each lazy entrypoint it contains a lambda that is called upon invocation. The entrypoint map is part of the contract's storage, but not visible within self.data.

For efficiency reasons the keys of the entrypoint map (its ids) are integers of type sp.TNat. The id for entrypoint ep can be obtained as sp.entrypoint_id("ep").

Using these expressions, the entrypoint map can be arbitrarily manipulated. For example, an entrypoint can be updated with the following command:

python
sp.entrypoint_map()[sp.entrypoint_id("ep")] = ...

As a convenience, this can be abbreviated to:

python
sp.set_entrypoint("ep") = ...

We can check whether the entrypoint map currently has an entry for ep with the following expression:

python
sp.entrypoint_map().contains(sp.entrypoint_id("ep"))

Again, this can be abbreviated as:

python
sp.has_entrypoint("ep")

Note that the entrypoint map is accessible only from inside non-lazy entrypoints.

Entrypoints without implementation โ€‹

Initially the big map contains lambdas for all entrypoints that are marked as lazy, except for those marked as lazy_no_code. For example calling an entrypoint decorated with

python
@sp.entrypoint(lazify = True, lazy_no_code = True)

will result in an error until it has been set in the entrypoint map.

In scenarios โ€‹

From outside the contract (in a scenario) the entrypoint map is accessible as sp.contract_entrypoint_map(c), where c is the contract. The entrypoint ids are accessible as sp.contract_entrypoint_id(c,ย "ep").

Example โ€‹

Suppose we have a lazy entrypoint ep. In the same contract we can define a non-lazy entrypoint that allows the administrator to update it:

python
@sp.entrypoint
def update_ep(self, new_code):
    sp.verify(sp.sender == self.data.admin)
    sp.set_entry_point("ep", new_code)

In order to perform an update in a scenario, we first define the new entrypoint as a function:

python
def f(self, params):
    sp.verify(params == 42)

We then wrap the Python function f so that it can be passed to update_ep.

python
new_ep = sp.utils.wrap_entry_point("ep", entry_point)
c.update_ep(new_ep)

Calling entrypoints โ€‹

Entrypoints without parameters โ€‹

All entrypoints require an input type, this even applies when no parameters are provided.

The type used in this situations is TUnit.

python
class MyContract(sp.Contract):
    @sp.entrypoint
    def ep1():
        contract = sp.contract(sp.TUnit, sp.self_address, "ep2").open_some()
        sp.transfer(sp.unit, sp.tez(0), contract)

    @sp.entrypoint
    def ep2():
        pass

Define a default entrypoint โ€‹

Default entrypoints are useful to implement the same transfer behavior as implicit accounts.

(unit %default)
python
class MyContract(sp.Contract):
    @sp.entrypoint
    def default():
        pass

Or as an @sp.entrypoint argument

python
class MyContract(sp.Contract):
    @sp.entrypoint(name = "default")
    def ep():
        pass