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:
@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
withargument
while transferringamount
to it.smartpysp.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 ifdestination
is not an implicit account or a contract with an entrypoint named "default" that takes asp.unit
argument.smartpysp.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.smartpysp.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 argumentprivate_=...
.The data specified in
private_
has to be compile-time constant, for example one could not usesp.now
.A more detailed example:
smartpyclass 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
andprivate_
data insp.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.
smartpysp.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 withtag
. Ifwith_type=True
is given the type ofevent
is explicitly given in the compiled Michelson code.Examples:
smartpysp.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
:smartpysp.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 tosp.operations
, you way want to reverse it first:smartpyops = [] 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.
smartpyop1 = 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.
smartpyr = 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.