Skip to content
On this page

Operations

In order to interact with other contracts and accounts so-called operations can be emitted. In SmartPy each such operation is of type sp.operation.

Note on operation execution

During the execution of an entrypoint, operations (e.g. sp.transfer) are not immediately executed. Instead, they are collected in a list (see below). The execution of these operations takes place only after the entrypoint has completed.

This means that changes in e.g. the contract balance are not reflected immediately:

smartpy
@sp.entrypoint
def ep(self, destination):
    b = sp.balance
    sp.transfer(sp.unit, sp.tez(5), destination)
    assert b == sp.balance  # sp.balance is unchanged at this point

Note on operation execution order

Operations are executed in FIFO (First In, First Out) order, which results in a DFS (depth first search) tree execution.

For example, suppose contract A's entrypoint first calls contract B's and then contract C's entrypoint. In turn, B's entrypoint calls one of D's entrypoints. Then the operations are executed in the following order: A, B, D, C

Operation statements

sp.transfer(argument: t, amount: sp.mutez, destination: sp.contract[t]) → 

Calls the contract at destination with argument while transferring amount to it.

smartpy
sp.transfer(100, sp.mutez(0), c)
sp.transfer(42, sp.mutez(0), sp.self_entrypoint("abc"))
sp.send(destination: sp.address, amount: sp.mutez) → 

Sends the specified amount to destination. Fails if destination is not an implicit account or a contract with an entrypoint named "default" that takes a sp.unit argument.

smartpy
sp.send(dest, sp.tez(42))

# equivalent to
sp.transfer((), sp.tez(42), sp.contract(sp.unit, dest).unwrap_some())
sp.create_contract(sp.Contract, delegate: sp.option[sp.public_key_hash], amount: sp.mutez, storage: t, private_: t1 | None) → sp.address

Originates a contract with the given storage, private data (if necessary) and delegate while transferring amount to it. Returns the new contract's address.

smartpy
sp.create_contract(MyContract, None, sp.tez(0), ())
sp.create_contract(MyContract, sp.Some(key_hash), sp.tez(10),
    sp.record(x=42, y="abc"), private_=sp.record(a=1, b="xyz"))

The private_ argument should be omitted if private data is not used in the contract, otherwise it can be stated as a positional argument or keyword argument private_=....

The data specified in private_ has to be compile-time constant, for example one could not use sp.now.

A more detailed example:

smartpy
class MyContract(sp.Contract):
    def __init__(self):
        self.private.px = 10
        self.private.py = 0
        self.data.a = sp.int(0)
        self.data.b = sp.nat(0)

    @sp.entrypoint
    def ep(self, params):
        self.data.a += params.x + self.private.px
        self.data.b += params.y + self.private.py

class Originator(sp.Contract):
    @sp.entrypoint
    def ep(self):
        _ = _sp.create_contract(
            MyContract,
            None,
            sp.tez(0),
            sp.record(a=1, b=2)
            private_=sp.record(px=10, py=20)
        )

Please note that the values specified for the storage and private_ data in sp.create_contract are set directly on the resulting dynamic contract. The __init__ function is not invoked when contracts are created dynamically.

sp.set_delegate(d: sp.option[sp.key_hash]) → 

Sets or removes the contract's delegate.

smartpy
sp.set_delegate(sp.Some(d)) # set the delegate to d
sp.set_delegate(None)       # remove the delegate
sp.emit(event: t, tag="...", with_type=[True|False]) → 

Emits event as an event, optionally tagged with tag. If with_type=True is given the type of event is explicitly given in the compiled Michelson code.

Examples:

smartpy
sp.emit("Hello")
sp.emit("World", tag="mytag")
sp.emit(sp.record(a="ABC", b="XYZ"), tag="mytag2", with_type=True)

Manual operation management

SmartPy keeps a running list of operations in sp.operations. In most cases you don't need to deal with it directly -- the statements described in the previous section simply add new elements to the beginning of this list. At the end of the entrypoint the operations in sp.operations are then executed in reverse order.

In contrast to the staments described above, manually managed operations have to be added explicitly to sp.operations if they are meant to be executed. Otherwise, they will be ignored.

sp.operations  → 

The operation list can be accessed as sp.operations:

smartpy
sp.operations = []  # remove any previously added operations

# Add new operations:
op1 = sp.transfer_operation(100, sp.mutez(0), c)
op2 = sp.transfer_operation(100, sp.mutez(0), c)
sp.operations.push(op1)
sp.operations.push(op2)

# `sp.operations` is now `[op2, op1]`. When the entrypoint finishes,
# it is executed in reverse order: first `op1`, then `op2`

Add a list of operations

The operations in sp.oeprations are executed in reverse order. Therefore, when adding a list of operations to sp.operations, you way want to reverse it first:

smartpy
ops = []
ops.push(sp.transfer_operation(100, sp.mutez(0), c))
ops.push(sp.transfer_operation(200, sp.mutez(0), c))
for op in reversed(ops):
    sp.operations.push(op)
sp.transfer_operation(argument: t, amount: sp.mutez, destination: sp.contract[t]) → sp.operation

Returns a transfer operation.

smartpy
op1 = sp.transfer_operation(100, sp.mutez(0), c)
op2 = sp.transfer_operation(42, sp.mutez(0), sp.self_entrypoint("abc"))
sp.operations.push(op1)
sp.operations.push(op2)
sp.create_contract_operation(sp.Contract, delegate: sp.option[sp.public_key_hash], amount: sp.mutez, storage: t, private_: t1 | None) → sp.record(address=sp.address, operation=sp.operation)

Returns a contract origination operation and the corresponding address.

smartpy
r = sp.create_contract_operation(
    MyContract,
    None,
    sp.tez(0),
    sp.record(a=1, b=2)
    private_=sp.record(px=10, py=20)
)
sp.operations.push(r.operation)
# r.address contains the address of the contract.
sp.set_delegate_operation(d: sp.option[sp.key_hash]) → 

Returns an operation that sets the delegate.

smartpy
op = sp.set_delegate_operation(sp.Some(d))
sp.operations.push(op)