Warning
|
This documentation is deprecated and no longer maintained. Please use the more recent https://SmartPy.io/docs. |
SmartPy is an intuitive and powerful smart contract development platform for Tezos.
The SmartPy language is available through a Python library for
building and analyzing Tezos smart contracts.
It comes with various tools: a compiler that generates Michelson code,
a simulation engine, a contract explorer, etc.
Building Blocks
This mechanism is useful because it brings very powerful meta-programming capabilities, as explained later.
Expressions
Like most languages, SmartPy has expressions. For example
self.data.x
represents the contract storage field x
and 2
represents the number 2, whereas self.data.x + 2
represents their
sum.
Inside a contract, when we write
y = self.data.x + 2
we declare y
as an alias the SmartPy expression self.data.x + 2
.
This is not a command for SmartPy.
Commands
Commands do something. For example,
sp.verify(self.data.x > 2)
checks that the field x
is larger than 2
and raises an error if it
isn’t.
This is performed at run time, i.e., in the blockchain, once
translated into Michelson.
Entry Points
An entry point is a method of a contract class that can be called
from the outside. Entry points need to be marked with the @sp.entry_point
decorator. For example, the following entry point checks that the
argument given is larger than 2
:
@sp.entry_point
def check_big_enough(self, params):
sp.verify(params > 2)
sp.entry_point
-
Decorator to introduce an entry point.
See reference Store Value template for simple entry points examples.
sp.entry_point
can also take optional parameters such as an
alternative string name
, bool private
, lazify
and lazy_no_code
flags.
@sp.entry_point(name = "another_name", private = True)
def check_big_enough(self, params):
sp.verify(params > 2)
We can restrict an entry point to only appear in SmartPy but not in
the generated Michelson contract. This is useful to implement checks
or initialization in SmartPy after origination in the test but before
real blockchain origination. One can use the full SmartPy machinery
such as sp.verify
, etc.
See reference Private
Entry Point template.
This is also useful to build custom UI through simulation by adding:
@sp.entry_point(private = True)
def set_y(self, params):
self.data.y = params
Return values
Entry points 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 value
.
See Lambdas for examples with sp.private_lambda
.
Lazy and updatable entry points
Lazy and updatable entry points are similar to regular SmartPy entry points with two special abilities: they are loaded on-demand (this reduces gas usage) and can be updated with specific operators.
See reference Lazy entry points template.
Default behavior
Entry points can all be "lazified" by default by using the flag
"lazy-entry-points"
but it’s usually smarter to select the lazy
entry points.
See example in Send back template.
Declaring a lazy entry point
We can ask SmartPy to "lazify" an entry point:
@sp.entry_point(lazify = True)
Furthermore, a lazy entry point can be initially excluded from the big map:
@sp.entry_point(lazify = True, lazy_no_code = True)
In this case, calling it will result in an error until it has been updated.
Updating entry points
In order to perform an update in a scenario, we first define the new entry point as a function, e.g.:
def f(self, params):
sp.verify(params == 42)
sp.set_entry_point(name, entry_point)
-
Update the lazy entry point called
name
(which must be a constant string) with the code inentry_point
.
The code is stored as a lambda inside a big map that is part of the contract’s storage. Such an entry point can be updated (from inside any non-lazy entry point), e.g.:
@sp.entry_point
def update_ep(self, new_code):
sp.verify(sp.sender == self.data.admin)
sp.set_entry_point("ep", new_code)
sp.utils.wrap_entry_point(name, entry_point)
-
A wrapper to prepare the a python function to be used as an update for entry point
name
(which must be a constant string).
c.update_ep(sp.utils.wrap_entry_point("ep", f))
sp.has_entry_point(name)
-
Return a boolean expression that indicates whether an entry point called
name
(which must be a constant string) is presently part of the big map.
Contracts
A SmartPy contract consists of a state together with one or several
entry points. It is a class definition that inherits from
sp.Contract
. The constructor (__init__
) makes a call to
self.init
and initializes fields that make up the contract’s state.
class Hello(sp.Contract):
def __init__(self):
self.init(x = 0)
@sp.entry_point
def set_x(newX):
self.data.x = newX
@sp.entry_point
def check_largerthan_x(p):
sp.verify(p > self.data.x)
self.init(..)
-
Alias for
self.init_storage(arg = None, **kwargs)
self.init_storage(arg = None, **kwargs)
-
Set the storage of a contract with no initial storage.
arg
andkwargs
cannot be both present.kwargs
is a dictionary of fields that are added to the storage (or removed when value isNone
).
There are two different ways to callself.init_storage
in a contract:-
on a single value
self.init_storage(arg)
wherearg
is a SmartPy expression; -
on a list of fields with values,
self.init_storage(**kwargs)
wherekwargs
is a dictionary.
-
self.update_initial_storage(arg = None, **kwargs)
-
Update the storage of a contract.
arg
andkwargs
cannot be both present.kwargs
is a dictionary of fields that are added to the storage (or removed when value isNone
).
self.update_initial_storage(e)
replaces the former storage whileself.update_initial_storage(**kwargs)
expects the former storage to be a record and adds, changes or removes (when value isNone
) former fields. This is very useful when we use complex inheritance patterns.
class Hello(sp.Contract):
def __init__(self):
""" Alias for init_storage. """
self.init(x = 0)
class Hello(sp.Contract):
def __init__(self):
""" Initialize the storage to a record containing
a single field x of value 0. """
self.init_storage(x = 0)
class Hello(sp.Contract):
def __init__(self):
""" It's allowed to call update_initial_storage even if no storage is defined. """
self.update_initial_storage(x = 0)
class Hello(sp.Contract):
def __init__(self):
self.init_storage(x = 0, y = 1)
class Hello(sp.Contract):
def __init__(self):
""" Define x then add y. """
self.init_storage(x = 0)
self.update_initial_storage(y = 1)
class Hello(sp.Contract):
def __init__(self):
""" Define x and t then remove x and add y. """
self.init_storage(x = 0, t = 12)
self.update_initial_storage(x = None, y = 1)
self.init_type(t)
Types
Types are usually automatically infered and not explicitly needed.
SmartPy types are all of the form sp.T<TypeName>
.
Meta-Programming
The functions described here are used to construct a smart contract. Smart contracts are executed once they are deployed in the Tezos blockchain (although they can be simulated). This is indeed meta-programming: we can write a program that writes a program, i.e., constructs a contract.
Note that in the example self.data.x + 2
, the actual addition
isn’t carried out until the contract has been deployed and the entry
point is called.
Constants vs Expressions
It is usually quite natural when an expression needs to be computed
on-chain vs a constant that is readily available.
By default, SmartPy fully computes at compile time expressions that are put in the
storage and doesn’t compute regular expressions that appear in smart contracts.
However, this behavior is not always desired or even possible.
Some specific expressions (such as sp.make_signature
, see
Signatures) need to be computed at compile time since there is no
corresponding construction in Michelson. This is done
automatically for mandatory ones.
This computation can be forced by using sp.resolve(..)
.
sp.resolve(e)
-
Force computation of
e
at compile time.
Typing
Type Inference
Just like in Python, most of the time there is no need to specify the type of an object in SmartPy. For a number of reasons (e.g., because SmartPy’s target language, Michelson, requires types), each SmartPy expression does however need a type. Therefore SmartPy uses type inference in order to determine each expressions type.
In practice, this means that information about an expression is
gathered according to its usage: for example, when somewhere in your
contract you write self.data.x == "abc"
, SmartPy will automatically
determine and remember that self.data.x
is a string.
Note that SmartPy types are distinct from Python types: self.data.x
== "abc"
has the Python type sp.Expr
(simply because it is a SmartPy
expression), whereas it has the SmartPy type sp.TBool
(see below).
While most of the time the user will not write many types explicitly it is beneficial to at least have a basic understanding of what they are. This also helps understanding error messages better.
Primitive Data Types Overview
SmartPy has the following primitive types:
sp.TUnit
-
A type with a single value, namely
sp.unit
.
See Unit. sp.TBool
-
The type of boolean values,
True
,False
,sp.bool(True)
andsp.bool(False)
.
See Booleans. sp.TInt
-
The type of integer values, e.g.
-42
orsp.int(-42)
.
See Integers. sp.TNat
-
The type of non-negative integer values, e.g.
sp.nat(42)
.
See Integers. sp.TIntOrNat
-
The type of integer values whose type is still undetermined between
sp.TInt
orsp.TNat
, e.g.42
orsp.intOrNat(42)
.
See Integers. sp.TString
-
The type of strings, e.g.
"abc"
orsp.string("abc")
.
See Strings. sp.TBytes
-
The type of serialized data, e.g.
sp.pack(42)
.
See Bytes.
Container Types
SmartPy has a few built-in data structures. Their types are:
sp.TPair
-
The type of pairs, e.g.
(1, True)
.
See Pairs. sp.TList
-
The type of lists, e.g.
[1, 2, 3]
.
See Lists. sp.TSet
-
The type of sets, e.g.
{1, 2, 3}
.
See Sets. sp.TMap
-
The type of maps, e.g.
{'A': 65, 'B': 66, 'C'; 67}
.
See Maps. sp.TBigMap
-
The type of lazy maps.
See Big Maps. sp.TOption
-
The type of optional values.
See Options.
There is no array in SmartPy because they are missing in Michelson,
we usually use maps instead. There are three helper functions:
sp.utils.vector(..)
, sp.utils.matrix(..)
and sp.utils.cube(..)
that take
respectively a list, a list of lists and a list of lists of lists
and return maps.
Convention. Container constructor names are uncapitalized and their types are capitalized.
sp.map(…)
of type sp.TMap(…)
, sp.big_map(…)
of type sp.TBigMap(…)
, sp.set(…)
of type sp.TSet(…)
, sp.list(…)
of type sp.TList
, sp.pair(…)
of type sp.TPair(…)
, etc.
Tezos-specific data types
A few data types are important in the context of smart contracts:
sp.TMutez
-
The type of Tezos tokens, e.g.
sp.mutez(42000)
stands for 0.042 Tez, wherassp.tez(42)
stands for 42 Tez.
See Mutez. sp.TTimestamp
-
A moment in time, e.g.
sp.timestamp(1571761674)
. The argument tosp.timestamp
is in "epoch" format, i.e. seconds since 1970-01-01.
See Timestamps. sp.TAddress
-
An address of a contract or account, e.g.
sp.address("tz1YtuZ4vhzzn7ssCt93Put8U9UJDdvCXci4")
.
See Contracts and Addresses. sp.TContract(t)
-
A contract whose parameter is of type
t
.
See Contracts and Addresses. sp.TKey
-
A public cryptographic key.
See Keys. sp.TKeyHash
-
The hash of a public cryptographic key.
See Key Hash. sp.TSignature
-
A cryptographic signature.
See Signatures. sp.TChainId
-
The type of chain identifiers, i.e., small identifiers for the different main and test Tezos blockchains.
See Chain Id. sp.TSecretKey
-
The type of secret keys. Secret keys cannot appear in smart contracts but Tests and Scenarios use them.
See Secret Key. sp.TSaplingState(memo_size = None)
sp.TSaplingTransaction(memo_size = None)
-
Types for Sapling integration.
See Sapling Integration. sp.TBls12_381_g1
sp.TBls12_381_g2
-
Points on the BLS12-381 curves G1 and G2, respectively.
Written as raw bytes, using a big-endian point encoding, as specifiedhere
. sp.TBls12_381_fr
-
An element of the scalar field Fr, used for scalar multiplication on the BLS12-381 curves G1 and G2.
Written as raw bytes, using a little-endian encoding.
See BLS12-381.
Record types, Variant types and layouts
In SmartPy, we can use custom data types called records and variants.
Records and variants are translated in Michelson into binary trees of pair
and or
with annotations corresponding to each field.
The geometry of these binary trees is described by a layout.
Layouts
A layout is a Python expression listing all fields or constructors in a binary tree structure such as ("b", ("a", "c"))
.
There is no equivalent instruction in Michelson.
See reference Data Type Layouts
template.
The SmartPy compiler recognizes a special format "<source> as
<target>"
to translate field names between SmartPy and Michelson. A
specific case "<source> as"
is also supported to generate
annotation-less types.
See FA1.2 template.
Record types
A record type represents a cartesian product of several types similar to a struct
in C
with a layout.
Please see the Records section.
sp.TRecord(**fields)
-
A record type is introduced by enumerating the field names together with types, e.g.,
sp.TRecord(x = sp.TInt, y = sp.TInt)
.
Asp.TRecord(**fields)
uses the default layout as determined by SmartPy (today a binary tree, it will change in the future). .layout(layout)
-
A record type, i.e. something of the form
sp.TRecord(…)
, can be used to define a record type with a layout by doing:
t = sp.TRecord(owner = sp.TAddress,
operator = sp.TAddress,
token_id = sp.TString)
t_with_layout = t.layout(("owner", ("operator", "token_id")))
.right_comb()
-
Like
.layout(..)
but the geometry used is(f1, (f2, .. (f_{k-1}, f_k)))))
, the list of fields is determined automatically and sorted alphabetically. .with_fields(**fields)
-
Add some fields to the
TRecord(..)
wherefields
is a Python (string, type)-dictionary of fields. .without_fields(fields)
-
Remove some fields from the
TRecord(..)
wherefields
is a Python list of fields.
Variant types
A variant type represents a union of several choices, similar to a clean version of
a struct with an enum/union pattern in C
.
Please see Variants.
sp.TVariant(**fields)
-
A variant type is introduced by enumerating the constructor names together with their inner types, e.g.
sp.TVariant(default_choice = sp.TInt, alternative_choice = sp.TString)
.
Asp.TVariant(**fields)
uses the default layout as determined by SmartPy (today a binary tree, it will change in the future). .layout(layout)
-
Similar to what happens for
TRecord
. .right_comb(layout)
-
Similar to what happens for
TRecord
.
Setting a type constraint in SmartPy
This is usually not needed for small contracts or prototypes but gets useful typically when interacting with the outside world, implementing a given interface, etc.
self.init_type(t)
-
Constrain the contract storage to be of type
t
(called in the__init__
constructor).
This is useful because it allows the user to make the storage type explicit or even to define storage-less contracts.
In a test, if not determined in the__init__
method, the storage can be initialized by callingc.init_storage(expression)
.
See reference Init Type Only template.
This is not mandatory but is appreciated by many in practice.
class MyContract(sp.Contract):
def __init__(self):
## Possibly set the storage type
self.init_type(sp.TRecord(a = sp.TInt, b = sp.TString))
## Possibly set the storage
self.init(...)
sp.set_type(expression, t)
-
Constrain
expression
to be of typet
. This can only be used as a command inside a contract.
There is no equivalent instruction in Michelson.
A usual pattern for big contracts is to explicitly setting types in the first lines of entry points.
This is usually not needed but is appreciated by many in practice.
@sp.entry_point
def my_entry_point(self, x, y, z):
## First set types
sp.set_type(x, sp.TInt)
sp.set_type(y, sp.TString)
sp.set_type(z, sp.TList(sp.TInt))
## Then do the computations
...
sp.set_result_type(t)
-
Wrap a block of commands and constrain its result type to
t
. This can only be used as a command inside a contract.
There is no equivalent instruction in Michelson.
This is e.g. useful for making types of failures explicit.
@sp.private_lambda()
def oh_no(self, params):
with sp.set_result_type(sp.TInt):
sp.if params > 0:
sp.failwith("too big")
sp.else:
sp.failwith("too small")
sp.set_type_expr(expression, t)
-
Constrain
expression
to be of typet
. This can only be used as an expression.
There is no equivalent instruction in Michelson.
A few words of caution about the differences between sp.set_type
and sp.set_type_expr
:
# Inside a contract:
@sp.entry_point
def my_entry_point(self, params):
...
## Warning: this is not taken into account (the expression is simply dropped).
sp.set_type_expr(params, sp.TInt)
...
@sp.entry_point
def my_entry_point(self, params):
...
## This is taken into account (when we call params afterward).
params = sp.set_type_expr(params, sp.TInt)
... params ...
@sp.entry_point
def my_entry_point(self, params):
...
## This is taken into account (sp.set_type is a command).
sp.set_type(params, sp.TInt) ## This is taken into account.
...
# Inside a test scenario:
scenario += ...
## This is illegal (command outside of a contract).
sp.set_type(..., ...)
## This is OK (usually useless nonetheless)
x = sp.set_type_expr(..., ...)
scenario += c.f(x)
Containers have built-in optional constraint arguments.
sp.map(l = …, tkey = …, tvalue = …)
-
Define a map of (optional) elements in
l
with optional key typetkey
and optional value typetvalue
. sp.big_map(l = …, tkey = …, tvalue = …)
-
Define a big_map of (optional) elements in
l
with optional key typetkey
and optional value typetvalue
. sp.set(l = …, t = …)
-
Define a set of (optional) elements in list
l
with optional element typet
. sp.list(l = …, t = …)
-
Define a list of (optional) elements in list
l
with optional element typet
.
Bounded types
SmartPy has the ability to limit any simple type’s values to a finite
subset. For example, the type sp.TBounded(["red", "green", "blue"])
has exactly three values: sp.bounded("red")
, sp.bounded("green")
,
sp.bounded("blue")
.
sp.unbounded
expects a bounded value and returns its unbounded
equivalent, e.g. it goes from sp.bounded("red")
to just "red" (of
type sp.TString
).
Note that bounded types can be based on any simple type,
e.g. sp.TBounded([1,2,3])
possible as well.
There is no cost associated to bounded types and values when compiling to Michelson.
Types and Operators
SmartPy expressions have the Python type sp.Expr
. In this class many
methods are overloaded so to provide convenient syntax: e.g. we can
write self.data.x + 2
for the SmartPy expression that represents the
sum of the storage field x
and 2.
The on-chain evaluation of an expression does not have any side effects except a possible failure (such as accessing a missing element in a map).
Any Python literal (string or integer) that is used in place of an
sp.Expr
is automatically converted.
Thus we can write self.data.x + 1
instead of self.data.x + sp.int(1)
.
BLS12-381
BLS12-381 is a paring-friendly elliptic curve.
SmartPy supports all associated Michelson types and constructions
including in tests in the interpreter.
See reference BLS12 template.
Types
sp.TBls12_381_fr
-
An element of the scalar field Fr, used for scalar multiplication on the BLS12-381 curves G1 and G2.
Written as raw bytes, using a little-endian encoding.
See Michelsonbls12_381_fr
. sp.TBls12_381_g1
sp.TBls12_381_g2
-
Points on the BLS12-381 curves G1 and G2, respectively.
Written as raw bytes, using a big-endian point encoding, as specifiedhere
.
See Michelsonbls12_381_g1
,bls12_381_g2
.
Operations
- e
-
Negate a curve point or field element.
Supported types:sp.TBls12_381_g1
|sp.TBls12_381_g2
|sp.TBls12_381_fr
See MichelsonNEG
. sp.to_int(n)
-
Convert a
sp.TBls12_381_fr
into ansp.TInt
.
See MichelsonINT
. n + m
-
Add two curve points or field elements.
Adding
sp.TBls12_381_g1
withsp.TBls12_381_g1
originatessp.TBls12_381_g1
Addingsp.TBls12_381_g2
withsp.TBls12_381_g2
originatessp.TBls12_381_g2
Addingsp.TBls12_381_fr
withsp.TBls12_381_fr
originatessp.TBls12_381_fr
See Michelson
ADD
. sp.mul(n, m)
-
Multiply a curve point or field element by a scalar field element.
Multiplying
sp.TBls12_381_g1
withsp.TBls12_381_fr
originatessp.TBls12_381_g1
Multiplyingsp.TBls12_381_g2
withsp.TBls12_381_fr
originatessp.TBls12_381_g2
Multiplyingsp.TBls12_381_fr
withsp.TBls12_381_fr
originatessp.TBls12_381_fr
Multiplyingsp.TInt
withsp.TBls12_381_fr
originatessp.TBls12_381_fr
Multiplyingsp.TNat
withsp.TBls12_381_fr
originatessp.TBls12_381_fr
Multiplyingsp.TBls12_381_fr
withsp.TInt
originatessp.TBls12_381_fr
Multiplyingsp.TBls12_381_fr
withsp.TNat
originatessp.TBls12_381_fr
See Michelson
MUL
. sp.pairing_check(pairs)
-
Verify that the product of pairings of the given list of points is equal to 1 in Fq12. Returns true if the list is empty. Can be used to verify if two pairings P1 and P2 are equal by verifying P1 * P2^(-1) = 1.
pairs must be of type
sp.TList(sp.TPair(sp.TBls12_381_g1, sp.TBls12_381_g2))
See Michelson
PAIRING_CHECK
.
Booleans
The type of SmartPy booleans is sp.TBool
.
The corresponding type in Michelson is
bool
.
SmartPy has the following logical operators:
- Literals:
True, False
-
The usual way to input bool literals in SmartPy.
- Literals:
sp.bool(b)
-
A literal of type
sp.TBool
whenb
is a Python bool literal (True
orFalse
). ~ e
-
Return the negation of
e
, wheree
must be a boolean.
See MichelsonNOT
. e1 | e2
-
Return
True
ife1
isTrue
, otherwisee2
. Bothe1
ande2
must be booleans.
SmartPy uses a lazy version of MichelsonOR
. e1 & e2
-
Return
False
ife1
isFalse
, otherwisee2
. Bothe1
ande2
must be booleans.
SmartPy uses a lazy version of MichelsonAND
. e1 ^ e2
-
Compute
e1
xore2
.
See MichelsonXOR
.
Note that, unlike in Python, &
and |
do
short-circuiting
on SmartPy boolean expressions: for example, the evaluation of
(x==x) | (self.data.xs[2] == 0))
will not fail.
Also, please note that not
, and
, and or
cannot be overloaded in
Python. Hence, we cannot use them to construct SmartPy expressions
and, as is customary for custom Python libraries,
we use ~
, &
, and |
instead.
Bytes
The type of byte arrays in SmartPy is sp.TBytes
.
The corresponding type in Michelson is
bytes
.
See reference Strings and Bytes template.
- Literals:
sp.bytes('0x…')
-
Introduce a
sp.TBytes
in hexadecimal notation. e1 + e2
-
Concatenate two bytes.
See MichelsonCONCAT
. sp.concat(l)
-
Concatenate a list
l
ofsp.TBytes
.
See MichelsonCONCAT
. sp.len(e)
-
Return the length of
e
.
See MichelsonSIZE
. sp.slice(expression, offset, length)
-
Slices
expression
fromoffset
forlength
characters.sp.slice(expression, offset, length)
is of typesp.TOption(sp.TBytes)
.
See MichelsonSLICE
. sp.pack(x)
-
Serialize a piece of data
x
to its optimized binary representation. Return an object of typesp.TBytes
.
See MichelsonPACK
. sp.unpack(x, t = …)
-
Parse the serialized data from its optimized binary representation. There is an optional argument
t
to fix the type.sp.unpack(e, t)
is of typesp.TOption(t)
.
See MichelsonUNPACK
. sp.blake2b(value)
sp.sha512(value)
sp.sha256(value)
-
The functions
sp.blake2b
,sp.sha512
,sp.sha256
take asp.TBytes
value and return the corresponding hash as a newsp.TBytes
value.
See MichelsonBLAKE2B
,SHA256
, andSHA512
.
See reference Hash Functions template. sp.keccak(value)
sp.sha3(value)
-
Introduced in Edo, the functions
sp.keccak
,sp.sha3
take asp.TBytes
value and return the corresponding hash as a newsp.TBytes
value.
See MichelsonKECCAK
andSHA3
.
Not supported yet in the interpreter. sp.utils.bytes_of_string(s)
-
Encode a constant string as
sp.TBytes
.
Chain Id
The type of chain identifiers in SmartPy is sp.TChainId
.
The corresponding type in Michelson is
chain_id
.
sp.chain_id
-
The id of the network currently evaluating the transaction.
See MichelsonCHAIN_ID
. - Literals:
sp.chain_id_cst("0x9caecab9")
-
Introducing a chain id by its hexadecimal representation.
Example: Chain Id
Please note that chain ids are non comparable. Equality can be verified by using sp.verify_equal
.
Comparable Types
- Comparison operators
==
,!=
,<
,<=
,>
,>=
-
The comparison operators
==
,!=
,<
,<=
,>
,>=
behave just like in python. They return a boolean. They are defined on SmartPy comparable types which extend Michelson’s.
See MichelsonEQ
,NEQ
,LT
,LE
,GE
,GT
, andCOMPARE
. sp.min(x, y)
sp.max(x, y)
-
sp.min(x, y)
andsp.max(x, y)
return the minimum and maximum ofx
andy
, respectively.
Contracts and Addresses
Following Michelson, there are two ways to point to other contracts in
SmartPy: typed sp.TContract(t)
for contracts with an entry point of
type t
and untyped sp.address
.
The corresponding types in Michelson are
contract
and
address
.
See Inter-Contract Calls (simple), On Chain Contract Calls - Collatz (advanced) and FA1.2 (applied, in the balance_of
entry point) templates.
- Literals:
sp.address("tz… or KT…")
-
Literals for addresses.
sp.self
-
The current contract of type
sp.TContract(t)
for some typet
.
See MichelsonSELF
. sp.self_entry_point(entry_point = '')
-
The optional entry point named
entry_point
of the current contract; of typesp.TContract(t)
wheret
is the type of the entry point’s parameters. Ifentry_point
is empty, use current entry point. sp.to_address(contract)
-
Compute the address, of type
sp.TAddress
, of a contract of typesp.TContract(t)
for some typet
.
See MichelsonADDRESS
. sp.self_address
-
Alias for
sp.to_address(sp.self)
.
This is the proper way to get a contract’s own address.
In tests, a contract’s address is accessible through theaddress
field. sp.self_entry_point_address(entry_point = "")
-
Alias for
sp.to_address(sp.self_entry_point(entry_point))
.
This is the proper way to get a contract’s own address of an entry point. sp.sender
-
The address that called the current entry point.
See MichelsonSENDER
. sp.source
-
The address that initiated the current transaction.
It may or may not be equal tosp.sender
.
See MichelsonSOURCE
. sp.contract(t, address, entry_point = "")
-
Cast an address of type
sp.TAddress
to an optional typed contract of typesp.TContract(t)
.-
When optional parameter
entry_point
is empty or unspecified, it returnssp.some(c)
, wherec
is a contract handle of typesp.TContract(t)
, ifaddress
, of typesp.TAddress
, points to a contract that expects a parameter of typet
. Otherwise it returnssp.none
. -
When
entry_point
is not empty, it returns the specific entry point specified by the stringentry_point
of the contract.t
must match the entry point’s expected parameter type. Otherwise, it returnssp.none
.
Due to restrictions of Michelson, it only works properly for contracts with multiple entry points.
See MichelsonCONTRACT
.
-
sp.transfer(arg, amount, destination)
-
Call the
destination
contract with argumentarg
while sending the specifiedamount
to it. Note thatdestination
must be of typesp.TContract(t)
. The type ofarg
must bet
, i.e. the argument sent to the destination must be consistent with what it expects.
See MichelsonTRANSFER_TOKENS
. sp.send(destination, amount, message = None)
-
Send the specified
amount
to thedestination
contract. Will fail with optional errormessage
ifdestination
(of typesp.TAddress
) does not resolve to a contract that expects asp.TUnit
argument (e.g. an account that does not result in any actions).
Abbreviation for:sp.transfer(sp.unit, amount, sp.contract(sp.TUnit, destination).open_some(message = message))
See MichelsonTRANSFER_TOKENS
. sp.implicit_account(key_hash)
-
See Key Hash for description.
See MichelsonIMPLICIT_ACCOUNT
.
Example: Suppose we have an address a
of a contract with an entry point "foo"
that
expects an integer. To call it, we first obtain a handle to the entry point:
c = sp.contract(sp.TInt, a, entry_point = "foo").open_some()
The call to open_some()
asserts that the address resolved successfully and that the
referenced entry point indeed expects an integer. Now that we have our handle c
, we
can call the contract e.g. with the argument -42
while sending along 0 tokens:
sp.transfer(-42, sp.mutez(0), c)
sp.create_contract(contract, storage = None, amount = tez(0), baker = None)
-
Create a new contract from stored SmartPy contract with optional storage, amount and baker.
See MichelsonCREATE_CONTRACT
.
See reference Create Contract template.
Voting Power
voting_powers
must be included in .run(…)
as described in Registering and Displaying Calls to Entry Points.
sp.total_voting_power
-
Return the total voting power of all contracts.
The total voting power coincides with the sum of the rolls count of every contract in the voting listings.
The voting listings is calculated at the beginning of every voting period.
See MichelsonTOTAL_VOTING_POWER
. sp.voting_power(key_hash)
-
Return the voting power of a specific delegate.
See Key Hash.
Level
sp.level
-
Like
sp.now
but for the level of the transaction. It is of typesp.TNat
.
Integers
There are two main types of integers in SmartPy: signed integers
sp.TInt
and non negative ones sp.TNat
.
The corresponding types in Michelson are
int
and
nat
.
SmartPy also uses a third definition sp.TIntOrNat
which stands for
integers that are not yet determined as sp.TInt
or sp.TNat
.
- Literals:
1, 2, 0, -5, etc.
-
Literal of type
sp.TIntOrNat
when non negative andsp.TInt
otherwise. The usual way to input literals in SmartPy, thanks to type inference. - Literals:
sp.int(i)
-
A literal of type
sp.TInt
wheni
is a Python integer literal. - Literals:
sp.nat(n)
-
A literal of type
sp.TNat
whenn
is a non negative Python integer literal. e1 + e2
,e1 - e2
,- e
,e1 * e2
,e1 % e2
,e1 // e2
,e1 << e2
,e1 >> e2
-
The usual arithmetic operators
+
,-
,*
,%
,//
,<<
,>>
behave just like in Python.
See MichelsonADD
,SUB
,NEG
,MUL
,EDIV
,LSL
andLSR
. e1 | e2
-
Compute bitwise
e1
ore2
fore1
ande2
of typesp.TNat
. Result is also of typesp.TNat
.
See MichelsonOR
. e1 & e2
-
Compute bitwise
e1
ande2
fore1
ande2
of typesp.TNat
. Result is also of typesp.TNat
.
See MichelsonAND
. e1 ^ e2
-
Compute bitwise
e1
xore2
fore1
ande2
of typesp.TNat
. Result is also of typesp.TNat
.
See MichelsonXOR
.
In SmartPy, type inference of arithmetic operators imposes that both sides have the same type. This constraint can be relaxed by explicitly using sp.to_int
.
Int vs Nat
abs(i)
-
Return the absolute value of
i
.abs
converts ansp.TInt
into asp.TNat
.
See MichelsonABS
. sp.to_int(n)
-
Convert a
sp.TNat
into ansp.TInt
.
See MichelsonINT
. sp.is_nat(i)
-
Convert a
sp.TInt
into ansp.TOption(sp.TNat)
.sp.is_nat(i) == sp.some(n)
wheni
is a non negativesp.TInt
andsp.none
otherwise.
See MichelsonISNAT
. sp.as_nat(i, message = None)
-
Convert an
sp.TInt
into asp.TNat
and fails if not possible with the optionalmessage
, i.e., wheni
is negative. It is implemented assp.as_nat(i) = sp.is_nat(i).open_some(message = message)
.
Division
e1 / e2
-
The
/
operator performs truncated integer division when applied to SmartPy expression, just like//
does. This is different to Python 3 (where/
doesn’t truncate and yields a float when applied to integers). sp.ediv(num, den)
-
Perform Euclidian division.
See MichelsonEDIV
.
Keys
Public Key
The type of public keys in SmartPy is sp.TKey
.
The corresponding type in Michelson is
key
.
- Literals:
sp.key('tz…')
-
A literal key is of the form
sp.key(s)
wheres
is a Python string. sp.hash_key(key)
-
See Key Hash.
sp.check_signature(k, s, b)
-
See Signatures.
Key Hash
The type of key hashes in SmartPy is sp.TKeyHash
.
The corresponding type in Michelson is
key_hash
.
See reference Baking Swap template.
- Literals:
sp.key_hash('tz…')
-
A literal key hash is of the form
sp.key_hash(s)
wheres
is a Python string'tz…'
. sp.hash_key(key)
-
Compute the base58check of
key
(which must be of typesp.TKey
).
It returns asp.TKeyHash
value.
See MichelsonHASH_KEY
. sp.set_delegate(baker)
-
Set or unset an optional
baker
of typesp.TOption(sp.TKeyHash)
.
See MichelsonSET_DELEGATE
.
In tests, a contract’s baker is accessible through thebaker
field. sp.implicit_account(key_hash)
-
Return the implicit account of type
sp.TContract(sp.TUnit)
from asp.TKeyHash
.
See MichelsonIMPLICIT_ACCOUNT
. sp.voting_power(key_hash)
-
Return the voting power of a given key hash.
This voting power coincides with the weight of the key hash in the voting listings (i.e., the rolls count) which is calculated at the beginning of every voting period.
See MichelsonVOTING_POWER
.
See [Other Operations] forsp.total_voting_power
.
Secret Key
The type of secret keys in SmartPy is sp.TSecretKey
.
There is no corresponding type in Michelson.
Secret keys are used in tests.
See Cryptography in Test Scenarios and Signatures.
Lambdas
The type of functions in SmartPy is sp.TLambda(t1, t2)
where t1
is the parameter type and t2
the result type.
The corresponding type in Michelson is
lambda
.
See reference Lambdas template.
sp.build_lambda(f)
-
Define a SmartPy lambda from a Python function or lambda.
For example,sp.build_lambda(lambda x: x + 3)
represents a function that takes an argumentx
and returnsx + 3
.
This function is usually useless as it is called automatically by SmartPy in most contexts.
See MichelsonLAMBDA
. sp.private_lambda(with_operations=False, with_storage=False, wrap_call=False)
-
Decorator to introduce a lambda that is also a private variable.
This is used for functions that are expected to be used more than
once. For functions that read or write the storage, specify
with_storage=True
. For functions that create operations,
with_operations=True
. Ifwrap_call=True
is set, the function can
be called as a command on its own. Values are returned by using
sp.result(value)
. + See reference WorldCalculator template. sp.global_lambda
-
Deprecated. Use
private_lambda
instead. Note that methods
decorated withprivate_lambda
take both aself
argument in
addition to the function argument.
class MyContract(sp.Contract):
...
@sp.private_lambda()
def square_root(self, x):
sp.verify(x >= 0)
y = sp.local('y', x)
sp.while y.value * y.value > x:
y.value = (x // y.value + y.value) // 2
sp.verify((y.value * y.value <= x) & (x < (y.value + 1) * (y.value + 1)))
sp.result(y.value)
@sp.entry_point
def h(self, params):
self.data.result = self.square_root(params)
See Michelson LAMBDA
.
sp.sub_entry_point
-
This has been deprecated in favour of
sp.private_lambda(with_operations=True, with_storage=True, wrap_call=True)
, which is equivalent.
class MyContract(sp.Contract):
def __init__(self):
self.init(x = 2, y = "aaa", z = 0)
@sp.sub_entry_point
def a(self, params):
sp.set_delegate(sp.none)
self.data.x += 1
sp.result(params * self.data.x)
@sp.entry_point
def f(self, params):
self.data.z = self.a(5) + self.a(10)
@sp.entry_point
def g(self, params):
self.data.z = self.a(6)
See Michelson LAMBDA
.
f(x)
-
Call a lambda.
Iff
is of typesp.TLambda(t1, t2)
andx
is of typet1
thenf(x)
is of typet2
.
See MichelsonEXEC
. f.apply(x)
-
Partially apply a lambda.
Iff
is of typesp.TLambda(sp.TPair(tp1, tp2), target)
andx
is of typetp1
thenf.apply(x)
is of typesp.TLambda(tp2, target)
.
See MichelsonAPPLY
.
Lists
The type of lists over type t
is sp.TList(t)
.
All elements need to be of the same type.
The corresponding type in Michelson is
list
.
See reference Lists template.
- Literals:
sp.list(l = …, t = …)
and standard Python lists -
Define a list of (optional) elements in
l
whose optional type ist
.
See MichelsonNIL
.
Standard Python lists are also accepted, e.g.,[1, 2, 3]
,["aa", "bb", "cc"]
. myList.push(element)
-
Push an element on top of a list.
See MichelsonCONS
. sp.len(myList)
-
Return the length of list
myList
.
See MichelsonSIZE
. sp.concat(myList)
-
Concatenate a list
myList
ofsp.TString
orsp.TBytes
.
See MichelsonCONCAT
. sp.range(x, y, step = 1)
-
A list from
x
(inclusive) toy
(exclusive). Useful in conjunction withsp.for
loops. myList.rev()
-
Reverse a list.
sp.for … in …:
-
Iterate on a list.
See MichelsonITER
andMAP
.
To iterate onsp.TMap(key, value)
orsp.TSet(elem)
, we first convert to ansp.TList(..)
withe.items()
,e.keys()
,e.values()
ore.elements()
.
@sp.entry_point
def sum(self, params):
self.data.result = 0
sp.for x in params:
self.data.result += x
sp.match_cons(l)
-
Match a list and expose its head and tail if any.
See reference Lists template.
with sp.match_cons(params) as x1:
self.data.head = x1.head
self.data.tail = x1.tail
sp.else:
self.data.head = "abc"
Please note that there is no way to perform random access on a list.
Maps and Big Maps
Maps
Maps in SmartPy are of type sp.TMap(key, value)
.
The corresponding type in Michelson is
map
.
- Literals:
sp.map(l = …, tkey = …, tvalue = …)
-
Define a map of (optional) elements in
l
(a Python dictionary) with optional key typetkey
and optional value typetvalue
.
See MichelsonEMPTY_MAP
andPUSH
. - Literals: standard Python dictionaries
-
Standard Python dictionaries are also accepted, e.g.,
{0 : "aa", 12 : "bb" }
.
See MichelsonEMPTY_MAP
andPUSH
. my_map[key] = value
-
Set or replace an element in a map.
See MichelsonUPDATE
. del my_map[key]
-
Delete an element from a map.
See MichelsonUPDATE
. my_map[key]
-
Look up an entry in a map. It fails if the entry is not found.
key
must have the type of its keys.
See MichelsonGET
. my_map.get(key, default_value = None)
-
Same as
e[key]
. Ifdefault_value
is specified and there is no entry forkey
inmy_map
, returnsdefault_value
instead of failing.
See MichelsonGET
. my_map.contains(key)
-
Check whether the map
my_map
contains thekey
.
See MichelsonMEM
. sp.len(my_map)
-
Return the size of the map
my_map
.
See MichelsonSIZE
. my_map.items()
-
Return the sorted list of key-value entries in a map (not a big_map). Each entry is rendered as record with the two fields
key
andvalue
. my_map.keys()
-
Return the sorted list of keys of a map (not a big map).
my_map.values()
-
Return the list of values of a map (not a big_map), sorted by keys.
sp.update_map(my_map, key, value)
-
Return a new copy of map
my_map
wherekey
has optional valuevalue
(sp.none
to delete). sp.get_and_update(my_map, key, value)
-
Return two elements: the previous optional value (
sp.none
if missing`) and a new map assp.update_map
would do.
It is typically used in the following way.
(previous_value, new_map) = sp.get_and_update(self.data.m, 1, sp.some("one"))
Big Maps
Big maps, of type sp.TBigMap(key, value)
, are lazy datastructures
that are only serialized and deserialized on demand.
The corresponding type in Michelson is
big_map
.
We cannot iterate on big maps or compute their sizes.
See reference Lists template.
- Literals:
sp.big_map(l = …, tkey = …, tvalue = …)
-
Define a big_map of (optional) elements in
l
with optional key typetkey
and optional value typetvalue
.
See MichelsonEMPTY_BIG_MAP
. my_big_map[key] = value
-
Set or replace an element in a big map.
See MichelsonUPDATE
. del my_big_map[key]
-
Delete an element from a big map.
See MichelsonUPDATE
. my_big_map[key]
-
Look up an entry in a big map. Fails if the entry is not found.
key
must have the type of its keys.
See MichelsonGET
. my_big_map.get(key, default_value = None)
-
Same as
my_big_map[key]
. Ifdefault_value
is specified and there is no entry forkey
inmy_big_map
, returnsdefault_value
instead of failing.
See MichelsonGET
. my_big_map.contains(key)
-
Check whether the big map
my_big_map
contains thekey
.
See MichelsonMEM
. sp.update_map(my_big_map, key, value)
-
Return a new copy of big map
my_big_map
wherekey
has optional valuevalue
(sp.none
to delete). sp.get_and_update(my_big_map, key, value)
-
Return two elements: the previous optional value (
sp.none
if missing`) and a new map assp.update_map
would do.
It is typically used in the following way.
(previous_value, new_map) = sp.get_and_update(self.data.m, 1, sp.some("one"))
Mutez
The type of amounts in SmartPy is sp.TMutez
.
The corresponding type in Michelson is
mutez
.
- Literals:
sp.tez(…)
andsp.mutez(…)
-
sp.tez(10)
andsp.mutez(500)
represent respectively 10 tez and 500 mutez. sp.amount
-
The amount of the current transaction.
See MichelsonAMOUNT
. sp.balance
-
The balance of the current contract.
See MichelsonBALANCE
.
Due to the unintuitive semantics in Michelson, we suggest that developers do not rely on balance too much. See Tezos Agora post.
In tests, a contract’s balance is accessible through thebalance
field. e1 + e2
ande1 - e2
-
Usual arithmetic operators on
sp.TMutez
.
See MichelsonADD
andSUB
. sp.split_tokens(amount, quantity, totalQuantity)
-
Compute
amount * quantity / totalQuantity
whereamount
is of typesp.TMutez
, andquantity
andtotalQuantity
are of typesp.TNat
. sp.ediv(num, den)
-
Perform Euclidian division.
See MichelsonEDIV
.
Never
An empty type sp.TNever
.
sp.never(expression)
-
Terminate a never branch.
See reference Never template.
Options
Optional values in SmartPy are of type sp.TOption(t)
.
The corresponding type in Michelson is
option
.
They represent values of type t
or nothing.
Optional values are useful for accomodating missing data: e.g. if your
contract has an optional expiry date, you can add a field expiryDate
= sp.none
to the constructor. Then, if you want to set the expiry
date, you write expiryDate = sp.some(sp.timestamp(1571761674))
.
Conversely, if you want to unset it again, you write expiryDate =
sp.none
. SmartPy automatically infers the type
sp.TOption(sp.TTimestamp)
for x
, so you don’t have to make it
explicit.
sp.some(e)
-
Define an optional value containing an element
e
.
See MichelsonSOME
. sp.none
-
Define an optional value not containing any element.
See MichelsonNONE
. e.is_some()
-
Check that an optional value contains an element, i.e., checks whether it is of the form
sp.some(…)
.
See MichelsonIF_NONE
. e.open_some(message = None)
-
If
e
is equal tosp.some(x)
, returnx
; otherwise fail.message
is the optional error raised in case of error.
See MichelsonIF_NONE
.
Pairs
Pairs in SmartPy are of type sp.TPair(t1, t2)
.
Furthermore, pairs can be matched using the following command:
sp.match_pair(x)
-
If
x
is a SmartPy pair (i.e. a value of typesp.TPair(t,u)
), this returns a Python pair of its components. This is typically used as follows:
x1, x2 = sp.match_pair(p)
Records
Records in SmartPy are of type sp.TRecord(**kargs)
where kargs
is
a Python dict
of SmartPy types indexed by strings.
They generalize the Michelson type
pair
.
- Literals:
sp.record(field1 = value1, field2 = value2, .., )
-
Introduce a record.
- Field access
-
If
x
is a record anda
one of its fields, we can obtain the field’s value by writingx.a
.
Furthermore, records can be matched using the following command:
sp.match_record(x, *fields)
-
If
x
is a SmartPy record (i.e. a value of typesp.TRecord(…)
), this returns a Python tuple of selected record fields. The list of fields can be all the record’s fields or just some of them. This is typically used as follows:
fld1, fld2, fld3 = sp.match_record(x, "fld1", "fld2", "fld3" )
sp.modify_record(x, name = None)
-
A variant of this command allows modifying a record that is held in the contract storage or in a local variable:
with sp.modify_record(self.data, "data") as data:
...
data.x = 12
data.y += 1
This command tells the SmartPy compiler to open the record, handle its
fields independently and recreate the record afterwards in a linear
way.
This command is mostly useful when dealing with tickets.
Sapling Integration
Sapling
is a new feature in Michelson.
SmartPy.io is compatible with Sapling in different ways: types, michelson implementation, compiler, and a fake implementation for test scenarios.
See reference Sapling and Sapling2 templates.
There are two types for Sapling: sp.TSaplingState(memo_size)
for
states and sp.TSaplingTransaction(memo_size)
for transactions where
memo_size
is a constant integer between 0
and 65535
and
corresponds to potentially included data in transaction. The
memo_size
parameter is optional in types sp.TSaplingState
and
sp.TSaplingTransaction
as it may be computed through type inference.
SmartPy doesn’t generate proper literals for Sapling types but an ad-hoc, fake, test implementation is provided for test scenarios. This is not a proper Sapling transaction but it is enough to test contracts (see reference templates).
- Literals:
sp.sapling_test_transaction(source, target, amount, memo_size)
-
This is a fake / test version.
source
-
source
is a Python string, empty string for minting tokens. target
-
target
is a Python string, empty string for burning tokens. amount
-
amount
is a non-negative Python integer. memo_size
-
memo_size
is a non-negative Python integer between0
and65535
. - Two operations
sp.sapling_empty_state(memo_size)
-
Building an empty
sp.TSaplingState
with a defaultmemo_size
(Expected length for message of Sapling transaction) memo_size
-
memo_size
is a uint16 value and represents the expected message length of the sapling transaction. (must be between 0 and 65535) sp.sapling_verify_update(transaction)
-
When
state
is asp.TSaplingState(memo_size)
andtransaction
asp.TSaplingTransaction(memo_size)
,sp.sapling_verify_update(state, transaction)
applies the transaction to the state, validates it and returnssp.none
if it fails andsp.some(x)
wherex
is of typesp.TPair(sp.TInt, sp.TSaplingState(memo_size))
.
Sets
Sets in SmartPy are of type sp.TSet(element)
.
The corresponding type in Michelson is
set
.
See reference Lists template.
- Literals:
sp.set(l = …, t = …)
-
Define a set of (optional) elements in list
l
with optional element typet
.
See MichelsonEMPTY_SET
. - Literals: standard Python sets
-
Sets can also be defined using regular Python syntax
{1, 2, 3}
. This only works with non-SmartPy specific expressions. For SmartPy expressions, we must usesp.set([e1, e2, …, en])
. mySet.elements()
-
Return the sorted list of elements in a set.
mySet.contains(element)
-
Check whether the set
mySet
contains theelement
.
See MichelsonMEM
. mySet.add(element)
-
Add an element to a set.
See MichelsonUPDATE
. mySet.remove(element)
-
Remove an element from a set.
See MichelsonUPDATE
.
Signatures
The type of signatures in SmartPy is sp.TSignature
.
The corresponding type in Michelson is
signature
.
See reference Signatures and State Channels templates.
sp.check_signature(k, s, b)
-
Determine whether the signature
s
(asp.TSignature
value) has been produced by signingb
(asp.TBytes
value) with the private key corresponding tok
(asp.TKey
public key value).
See MichelsonCHECK_SIGNATURE
. sp.make_signature(secret_key, message, message_format = 'Raw')
-
Forge a signature compatible with
sp.check_signature(…)
; themessage
is aTBytes
value (usually the result of ansp.pack
call), themessage_format
can also be"Hex"
in which case the message will be interpreted as an hexadecimal string.sp.make_signature
is not available for compilation to Michelson (a smart contract cannot manipulate secret keys). It can only be used in Tests and Scenarios.
Strings
The type of SmartPy strings is sp.TString
.
The corresponding type in Michelson is
string
.
See reference Strings and Bytes template.
- Literal strings
"…"
and'…'
-
Strings in SmartPy are introduced by simply using regular Python strings of the form
"…"
or'…'
, or by usingsp.string(s)
wheres
is a Python string. - Literals:
sp.string(s)
-
Alternative way to introduce SmartPy string literals when
s
is a python string. e1 + e2
-
Concatenates two strings.
See MichelsonCONCAT
. sp.concat(l)
-
Concatenates a list
l
of strings.
See MichelsonCONCAT
. sp.len(e)
-
Return the length of
e
.
See MichelsonSIZE
. sp.slice(expression, offset, length)
-
Slices
expression
fromoffset
forlength
characters.sp.slice(expression, offset, length)
is of typesp.TOption(sp.TString)
.
See MichelsonSLICE
.
Tickets
Tickets of content type t
have type sp.TTicket(t)
.
The corresponding type in Michelson is
ticket cty
.
See reference Tickets template.
The following operations are supported:
sp.ticket(content, amount)
-
Create a ticket with
content
andamount
. Ifcontent
is of typet
, the return type issp.TTicket(t)
. Theamount
has to be of typesp.TNat
.
See MichelsonTICKET
.
High Level Functions
sp.read_ticket(ticket)
-
Read the data of a
ticket
and return a Python object with four fieldsticketer
,content
,amount
, andcopy
.
read_ticket = sp.read_ticket(ticket)
The output read_ticket
object is not a SmartPy expression and
cannot be passed as an expression to regular SmartPy functions.
However, it can be passed to regular Python functions thanks to
meta-programming.
It is an error to access ticket
again after this command, but read_ticket.copy
must be used instead. If ticket
is of type sp.TTicket(t)
, then
read_ticket.ticketer
is of type sp.TAddress
, read_ticket.content
of type t
, and read_ticket.amount
of type sp.TNat
.
See Michelson READ_TICKET
.
sp.split_ticket(ticket, q1, q2)
-
Split
ticket
into two tickets of amountsq1
andq2
. These two amounts must sum up toticket
's value, otherwisesp.none
is returned. Ifticket
is of typesp.TTicket(t)
, this returns a value of typesp.TOption(sp.TPair(sp.TTicket(t), sp.TTicket(t)))
.
See MichelsonSPLIT_TICKET
. sp.join_tickets(ticket1, ticket2)
-
Return a new ticket with the sum of the amounts in
ticket1
andticket2
. The two tickets must have the same contents, or an error is raised. Both tickets must have the same typesp.TTicket(t)
, which is also the return type.
See MichelsonJOIN_TICKETS
.
Low Level Functions
sp.read_ticket_raw(ticket)
-
Like
sp.read_ticket(ticket)
when the output is two elements, similar to MichelsonREAD_TICKET
. sp.split_ticket_raw(ticket, q1, q2)
-
Like
sp.split_ticket(ticket, q1, q2)
when the output is two elements, similar to MichelsonSPLIT_TICKET
. sp.join_tickets_raw(tickets)
-
Like
sp.join_tickets(ticket1, ticket2)
wheretickets
is a pair of tickets, similar to MichelsonJOIN_TICKETS
.
Testing Tickets
sp.test_ticket(ticketer, content, amount)
-
Create a new ticket issued by the locally defined contract
ticketer
. This is meant to be used for testing purposes in scenarios.
Timestamps
The type of timestamps in SmartPy is sp.TTimestamp
.
The corresponding type in Michelson is
timestamp
.
See reference Timestamps template.
- Literals:
sp.timestamp(…)
-
A literal timestamp is defined by doing
sp.timestamp(i)
wherei
is an integer representing the number of seconds since epoch (January 1st 1970). sp.now
-
The minimal injection time on the stack for the current block/priority. For all reasonable purposes, this is a technical detail and
sp.now
should be understood as the timestamp of the block whose validation triggered the execution.
See MichelsonNOW
. sp.timestamp_from_utc(year, month, day, hours, minutes, seconds)
-
Compute a constant timestamp corresponding to an UTC datetime.
sp.timestamp_from_utc_now()
-
Compute a constant timestamp corresponding to now. This is fixed at contract or test evaluation time.
e.add_seconds(seconds)
-
Return a timestamp with
seconds
added toe
, wheree
must be asp.TTimestamp
andseconds
asp.TInt
.
See MichelsonADD
. e.add_minutes(minutes)
-
Return a timestamp with
minutes
added toe
, wheree
must be asp.TTimestamp
andminutes
asp.TInt
.
See MichelsonADD
. e.add_hours(hours)
-
Return a timestamp with
hours
added toe
, wheree
must be asp.TTimestamp
andhours
asp.TInt
.
See MichelsonADD
. e.add_days(days)
-
Return a timestamp with
days
added toe
, wheree
must be asp.TTimestamp
anddays
asp.TInt
.
See MichelsonADD
. e1 - e2
-
Return the difference in seconds between two timestamps.
See MichelsonSUB
.
Unit
Variants
Variants in SmartPy are of type sp.TVariant(**kargs)
where kargs
is a Python dict
of SmartPy types indexed by strings.
They generalize the Michelson type
or
.
They are used to define sum-types, similar to enums in other languages
with the extra feature that these enums contain values.
See reference Variant template.
sp.variant('constructor', value)
sp.left(value)
-
Introduce a left/right variant.
See MichelsonLEFT
. sp.right(value)
-
Introduce a left/right variant.
See MichelsonRIGHT
. e.is_variant(v)
-
For a variant, checks whether it is
sp.variant(v, …)
.
See MichelsonIF_LEFT
. e.is_left(v)
-
For a left/right variant, checks whether it is
sp.left(…)
.
See MichelsonIF_LEFT
. e.is_right(v)
-
For a left/right variant, checks whether it is
sp.right(…)
.
See MichelsonIF_LEFT
. e.open_variant(v)
-
If
e
is equal tosp.variant(v, x)
, returnx
. Otherwise fail.
See MichelsonIF_LEFT
.
Commands
Assignment
lhs = rhs
-
Evaluate
rhs
and assign it tolhs
. Bothlhs
andrhs
must be SmartPy expressions. Doesn’t work iflhs
is a Python variable. lhs.set(rhs)
-
Alternative syntax for assignment. Useful when the left-hand-side is a single Python variable, e.g. one referencing a SmartPy local variable (see below).
Local variables
Local SmartPy variables can be defined as follows:
x = sp.local("x", 0)
The first argument to sp.local
is a string that will be used in
error messages. It is advisable to use the same name that is used on
the left of =
.
Local variable values can be accessed to and updated with the .value
field:
x.value = 1
, x.value = 2 * x.value + 5
, etc.
This is mostly useful in loops.
Note that local SmartPy variables are different to Python variables. The latter cannot be updated during contract execution.
As an example, here is how we can commute a square root.
@sp.entry_point
def squareRoot(self, x):
sp.verify(x >= 0)
y = sp.local('y', x)
sp.while y.value * y.value > x:
y.value = (x // y.value + y.value) // 2
sp.verify((y.value * y.value <= x) & (x < (y.value + 1) * (y.value + 1)))
self.data.value = y.value
sp.compute(expression)
-
This is defined as a local variable immediately created and returned with its value.
def compute(expression):
return local("compute_%i" % (get_line_no()), expression).value
It’s used to evaluate an expression and remember its value.
When we evaluate this code:
x = sp.compute(self.data.a)
self.data.a += 1
y = sp.compute(self.data.a)
Then y
contains a value equals to the value of x
plus one.
Control and Syntactic Sugar
Since Python doesn’t allow its control statements to be overloaded,
certain language constructs are desugared by a pre-processor: sp.if
,
sp.else
, sp.for
, sp.while
are SmartPy commands. (The desugared
version has sp.if_
etc. instead.)
sp.if …:
sp.else:
-
A
if
condition that is evaluated on-chain. sp.for … in …:
-
A
for
loop that is evaluated on-chain.
sp.for x in params:
self.data.result += x
sp.while …:
-
A
while
loop that is evaluated on-chain.
sp.while 1 < y:
self.data.value += 1
y.set(y // 2)
See Michelson LOOP
.
If we use e.g. sp.if
instead of a plain if
, the result will be a
SmartPy conditional instead of a Python one. SmartPy conditionals are
executed once the contract has been constructed and has been deployed
or is being simulated. On the other hand, Python conditionals are
executed immediately. Therefore the condition after the if
cannot
depend on the state of the contract. When in doubt, always use the
sp.
prefix inside a smart contract.
Checking a Condition
sp.verify(condition, message = …)
-
Check that the boolean expression
condition
evaluates toTrue
and raises an error if it doesn’t. This is useful to prevent an entry point from proceding if certain conditions are not met (e.g. in a contract that manages accounts a client cannot withdraw more money than they deposited).An optional parameter
message
is raised if condition is not met. Whenmessage
is not present, an exception of the formWrongCondition: …
is raised.See Michelson
FAILWITH
. sp.verify_equal(v1, v2, message = …)
-
It serves the same purpose by checking equality between
v1
andv2
. This works on both comparable and non-comparable types.
Raising Exceptions
Once an exception is raised, it cannot be caught.
String error messages may take a lot of space in smart contracts so we try to be cautious there.
sp.failwith(message)
-
Abort the current transaction and raises a
message
of arbitrary type. This cannot be caught.
See MichelsonFAILWITH
.
Besides sp.verify
and sp.verify_equal
, exceptions can also be raised by other constructions:
- Accessing fields
my_map[x]
-
The exception raised is now a pair containing the
(x, my_map)
. - Opening variants
-
This may fail with
sp.unit
. - Dividing by zero
-
A message is shown.
- Variable step in a range which is 0
-
A message is shown.
Flags
SmartPy supports two sorts of flags: boolean flags and flags with arguments.
Boolean Flags
Flag |
Role |
Default value |
disable-dup-check |
Remove the |
False |
dump-michel |
Dump Michel intermediate language |
False |
erase-comments |
Remove compiler comments |
False |
erase-var-annots |
Remove annotations (with |
False |
initial-cast |
Add an initial |
False |
pairn |
Use |
True |
simplify |
Allow simplification after compilation |
True |
simplify-via-michel |
Use Michel intermediate language |
False |
single-entry-point-annotation |
Emit parameter annotations for single entry point contracts |
True |
warn-unused |
Exceptions for unused entry point parameters |
True |
decompile |
Decompile after compilation (for test purposes) |
False |
Other Flags
Flag |
Role |
Values |
Default value |
protocol |
Protocol used in the scenario |
delphi, edo, florence, granada |
Florence |
lazy-entry-points |
Use big maps to store entry points |
none, single, multiple |
none |
exceptions |
Control exception generation in the compiler |
full-debug, debug-message, verify-or-line, default-line, line, default-unit, unit |
verify-or-line |
default_variant_layout |
Select default layout for variants |
tree, comb |
tree |
default_record_layout |
Select default layout for records |
tree, comb |
tree |
Protocol and default layout flags are initial which means that they must be set either in the command line or in the first steps in scenarios.
Adding Flags in a Scenario
self.add_flag(flag, *args)
-
Add a
flag
whereflag
is a string constant andargs
are potential arguments.
For example, you can writeself.add_flag("lazy-entry-points", "single")
orself.add_flag("erase-comments")
orself.add_flag("no-erase-comments")
.
To enable Edo specific compilation, use self.add_flag("protocol",
"edo")
or self.add_flag("protocol", "delphi")
for Delphi.
"florence"
is the current default. Choices are currently "delphi"
,
"edo"
, "florence"
(default) and "granada"
.
Erasing comments
A flag to remove comments that do not come from types in the generated Michelson.
See reference
Exception Optimization Levels template.
This is done by calling self.add_flag("erase-comments")
in the contract.
Exception Optimization Levels
Exception reporting is determined for a contract by setting self.exception_optimization_level = <level>
.
See reference Exception Optimization template.
Different levels are:
- "full-debug"
-
This is extremely costly in terms of size and gas. Useful for debugging purposes. Type of failure, line number, some parameters.
- "debug-message"
-
This is still very costly in terms of size and gas.
- "verify-or-line"
-
This is the default. Puts messages for
sp.verify
andsp.failwith
, and line numbers for other failures. - "default-line"
-
Puts messages for
sp.verify
with custom messages andsp.failwith
, and line numbers for other failures. - "line"
-
Only puts line numbers everywhere.
- "default-unit"
-
Puts messages for
sp.verify
with custom messages, andsp.failwith
, and unit for other failures. - "unit"
-
Always puts unit.
New features in Florence
Florence flags
self.add_flag("protocol", "florence")
- Enables Florence features.
Order of Evaluation of Contract Calls
In Florence, contract calls are switching to the DFS ordering.
New expected features in Granada (protocol 10)
Granada protocol
self.add_flag("protocol", "granada")
- Enables Granada features.
Utils
Methods
sp.utils.same_underlying_address(addr1, addr2)
-
It returns a boolean that informs if an address
A%foo
has the same underlying address asA
.
sp.verify(
sp.utils.same_underlying_address(
sp.address("KT1Tezooo1zzSmartPyzzSTATiCzzzyfC8eF%foo"),
sp.address("KT1Tezooo1zzSmartPyzzSTATiCzzzyfC8eF")
),
message = "Not the same underlying address"
)
sp.utils.mutez_to_nat(…)
-
Convert a
TMutez
value toTNat
. sp.utils.nat_to_mutez(n)
andsp.utils.nat_to_tez(n)
-
Convert a
TNat
value toTMutez
.
Experimental Features
Views
sp.utils.view(t, message = None)
-
Decorator
sp.utils.view
to introduce an entry point that happens to be a view.
A view is an entry point that-
Doesn’t change the storage;
-
Calls a callback
sp.TContract(t)
.
-
@sp.utils.view(sp.TNat)
def getBalance(self, params):
sp.result(self.data.balances[params].balance)
This code is a simpler version of the equivalent:
@sp.entry_point
def getBalance(self, params):
__s3 = sp.local("__s3", self.data.balances[sp.fst(params)].balance)
sp.set_type(sp.snd(params), sp.TContract(sp.TNat))
sp.transfer(__s3.value, sp.tez(0), sp.snd(params))
Importing SmartPy and Python code
Importing regular Python code can be done in any template with the regular Python import
.
Importing SmartPy code that uses the syntactic sugar is also doable but needs specific functions.
sp.io.import_template(template_name)
-
Import a template.
It only works in https://SmartPy.io, not with the CLI.
FA2 = sp.import_template("FA2.py")
class my_token(FA2.FA2):
...
sp.io.import_script_from_url(url, name = None)
-
Same functionality but instead of using a template from within SmartPy.io; import any file. The
url
is a string of the formhttp://
,https://
,file://
,file:
, etc. The module obtained name is the optionalname
parameter, with default equal tourl
. sp.io.import_stored_contract(name)
-
Same functionality but instead of importing a file from a
url
, import a script saved in the browser local storage.
It only works in https://SmartPy.io, not with the CLI. sp.io.import_script_from_script(name, script)
-
Import some script where both
name
andscript
are strings.
Metadata support
Helper function for
TZip
16 Standard.
See reference Metadata template.
sp.utils.metadata_of_url(url)
-
Initialize some metadata field in a storage with a constant
url
string.
Simple alias forsp.big_map({"" : sp.utils.bytes_of_string(url)})
. self.init_metadata(name, expression)
-
Generate a JSON metadata document for string
name
containing an arbitraty constant Pythonexpression
converted into JSON. sp.offchain_view(pure = False, doc = None)
-
Decorator to introduce an offchain view. Two optional parameters:
pure
(default isFalse
) to declare purity of view (dependent only on storage and parameters) anddoc
to set its documentation. Ifdoc
isNone
, the documentation is the docstring of the method.
@sp.offchain_view(pure = True)
def get_x(self, params):
"""blah blah ' fdsfds " """
sp.result(sp.record(a = self.data.x, b = 12 + params))
Debugging contracts
sp.trace(expression)
-
Compute an expression and print its value to std::out (or the console in a web-browser).
scenario.simulation
-
Graphical user interface and step by step simulation in a web-browser.
Returning and Binding data
This is an advanced topic and need not be looked at for regular SmartPy developers.
In a block, we can write sp.result(expr)
to compute expr
and
prepare the local result.
This data can be bound by using sp.bind_block()
.
See reference Bind template.
In this example, we define a block b
, computes some value and return
it.
def f(self, params)
b = sp.bind_block()
with b:
sp.if params > 12:
sp.result(self.data.x + params)
else:
sp.result(self.data.x + 2)
return b.value
Michelson Code Inlining
See reference Inline Michelson template.
sp.michelson(code, types_in, types_out)
-
Inline arbitrary Michelson code.
types_in
andtypes_out
are lists of types, andcode
is some Michelson code using the micheline syntax. Currently, in practice, only singletontypes_out
are supported.
self.data.x = sp.michelson("ADD;", [sp.TInt, sp.TInt], [sp.TInt])(15, 16)
self.data.y = sp.michelson("DUP; DIG 2; ADD; MUL;", [sp.TInt, sp.TInt], [sp.TInt])(15, 16)
sp.lambda_michelson(code, source = None, target = None)
-
Special case of
sp.michelson
where the code represents a lambda.
sp.lambda_michelson(code, t1, t2)
is of typesp.TLambda(t1, t2)
.
self.data.f = sp.lambda_michelson("DUP; PUSH int 2; ADD; MUL;", sp.TNat, sp.TInt)
Tests and Scenarios
General Framework
Scenarios describe a sequence of actions: originating contracts, computing expressions, calling entry points, etc.
They are directly used in SmartPy tests.
SmartPy currently supports two uses for scenarios: tests and compilation targets.
Adding a Test
sp.add_test(name, shortname=None, profile=False, is_default=True)
-
Adding a test.
sp.test_scenario()
-
Define a scenario.
Tests are added by :
@sp.add_test(name = "First test")
def test():
scenario = sp.test_scenario()
c1 = MyContract()
scenario += c1
scenario += c1.my_entrypoint(...)
...
Besides name
, sp.add_test
accepts several parameters.
shortname=None
-
Optional parameter. Short names need to be unique. Used in smartpy-cli outputs.
profile=False
-
Computes and pretty-prints profiling data.
is_default=True
-
Determines if the test is performed by default when evaluating all tests. Can be typically used in conjonction with
sp.in_browser
in templates to improve speed in browser.
See reference FA2 template.
Test Example
@sp.add_test(name = "First test")
def test():
# We define a test scenario, called scenario,
# together with some outputs and checks
scenario = sp.test_scenario()
# We first define a contract and add it to the scenario
c1 = MyContract(12, 123)
scenario += c1
# And send messages to some entry points of c1
scenario += c1.my_entrypoint(12)
scenario += c1.my_entrypoint(13)
scenario += c1.my_entrypoint(14)
scenario += c1.my_entrypoint(50)
scenario += c1.my_entrypoint(50)
scenario += c1.my_entrypoint(50).run(valid = False) # this is expected to fail
# Finally, we check the final storage of c1
scenario.verify(c1.data.myParameter1 == 151)
# and its balance
scenario.verify(c1.balance == sp.tez(0))
Inside a Scenario
Registering and displaying contracts
scenario += c1
# This is identical to doing
scenario.register(c1, show = True)
# To only register the smart contract but not show it
scenario.register(c1)
Contract Methods
Additionaly to entry points, contracts have two additional methods that can be called once, before origination.
c.set_initial_balance(expression)
-
Set the initial balance of a contract.
Test Accounts
Test accounts can be defined by calling sp.test_account(seed)
where seed
is a string.
A test account account
contains some fields: account.address
,
account.public_key_hash
, account.public_key
, and
account.secret_key
.
See Cryptography in Test Scenarios.
admin = sp.test_account("Administrator")
alice = sp.test_account("Alice")
bob = sp.test_account("Robert")
They can be used for several purposes: getting addresses with account.address
, in sender
or source
parameters or for checking or creating signatures.
Registering and Displaying Calls to Entry Points
c1.my_entrypoint(12)
c1.my_entrypoint(...).run(sender = ..., source = ...,
amount = ..., now = ..., level = ..., valid = ..., exception = ..., show = ..., chain_id = ..., voting_powers = ...)
The run
method and its parameters are all optional.
sender
-
the simulated sender of the transaction. It populates
sp.sender
. It can be either built by asp.test_account(…)
or an expression of typesp.TAddress
. source
-
the simulated source of the transaction. It populates
sp.source
. It can be either built by asp.test_account(…)
or an expression of typesp.TAddress
. amount
-
the amount sent. Example:
amount = sp.tez(10)
oramount = sp.mutez(10000)
. It populatessp.amount
. now
-
the timestamp of the transaction. Example:
sp.timestamp(1571761674)
. It populatessp.now
. level
-
the level of the transaction. Example:
1234
. It populatessp.level
. show
-
show or hide the transaction.
True
by default. valid
-
the expected validity of the transaction.
True
by default. If the validity of a transaction doesn’t match its expected validity, SmartPy shows an alert. exception
-
the expected exception raised by the transaction. If present,
valid
must beFalse
. chain_id
-
the simulated chain_id for the test. Example:
sp.chain_id_cst("0x9caecab9")
. voting_powers
-
the simulated voting powers for the test. Example:
voting_powers = { sp.key_hash("tz1…") : 10 }
.
Adding Document Information
scenario.h1("a title")
scenario.h2("a subtitle")
scenario.h3(..)
scenario.h4(..)
scenario.p("Some text")
Showing Expressions
To compute expressions, we use scenario.show(expression, html = True, stripStrings = False)
.
scenario.show(expression, html = True, stripStrings = False)
# html: True by default, False to export not in html but like in source code.
# stripStrings: False by default, True to remove quotes around strings.
scenario.show(c1.data.myParameter1 * 12)
scenario.show(c1.data)
Computing Expressions
To compute expressions, we use scenario.compute
.
x = scenario.compute(c1.data.myParameter1 * 12)
The variable x
can now be used in the sequel of the scenario and its value is fixed.
Accessing Data associated to Contracts
When c
is a contract in a scenario, we can access some associated data:
c.data
-
Retrieve its storage.
c.balance
-
Retrieve its balance.
c.baker
-
Retrieve its optional delegated baker.
c.address
-
Retrieve its address within the scenario.
In storage or similar circumstances, contracts get addresses of the form:"KT1TezoooozzSmartPyzzSTATiCzzzwwBFA1"
,"KT1Tezooo1zzSmartPyzzSTATiCzzzyfC8eF"
,"KT1Tezooo2zzSmartPyzzSTATiCzzzwqqQ4H"
,"KT1Tezooo3zzSmartPyzzSTATiCzzzseJjWC"
,"KT1Tezooo4zzSmartPyzzSTATiCzzzyPVdv3"
,"KT1Tezooo5zzSmartPyzzSTATiCzzzz48Z4p"
,"KT1Tezooo6zzSmartPyzzSTATiCzzztY1196"
,"KT1Tezooo7zzSmartPyzzSTATiCzzzvTbG1z"
,"KT1Tezooo8zzSmartPyzzSTATiCzzzzp29d1"
,"KT1Tezooo9zzSmartPyzzSTATiCzzztdBMLX"
,"KT1Tezoo1ozzSmartPyzzSTATiCzzzw8CmuY"
, etc. c.typed
-
Retrieve its testing typed contract value.
To access entry points, one can use field notation, e.g.,c.typed.my_entry_point
to access typed entry pointmy_entry_point
ofc
.
See reference FA1.2 template.
Dynamic Contracts
See reference Create Contract template.
Internally, SmartPy uses two types of contracts: static ones and
dynamic ones. Static contracts appear explicitly in the
scenarios. Dynamic ones are created in other
contracts executed in the scenario (with sp.create_contract
).
my_dynamic_contract = scenario.dynamic_contract(contractId, tcontract, tparameter)
-
Declare that a dynamic contract of dynamic
id
(an integer) is created with the corresponding storage and full parameter types.
The first dynamically createdcontractId
is0
, then '1', etc.
Return a dynamic contract that contains regular fieldsdata
,balance
,baker
,address
andtyped
and acall
method.
Dynamic contracts addresses are of the form:
"KT1TezoooozzSmartPyzzDYNAMiCzzpLu4LU"
,
"KT1Tezooo1zzSmartPyzzDYNAMiCzztcr8AZ"
,
"KT1Tezooo2zzSmartPyzzDYNAMiCzzxyHfG9"
,
"KT1Tezooo3zzSmartPyzzDYNAMiCzzvqsJQk"
,
"KT1Tezooo4zzSmartPyzzDYNAMiCzzywTMhC"
,
"KT1Tezooo5zzSmartPyzzDYNAMiCzzvwBH3X"
,
"KT1Tezooo6zzSmartPyzzDYNAMiCzzvyu5w3"
,
"KT1Tezooo7zzSmartPyzzDYNAMiCzztDqbVQ"
,
"KT1Tezooo8zzSmartPyzzDYNAMiCzzq2URWu"
,
"KT1Tezooo9zzSmartPyzzDYNAMiCzzwMosaF"
,
"KT1Tezoo1ozzSmartPyzzDYNAMiCzzzknqsi"
, etc.
my_dynamic_contract.call(entry_point, parameter)
-
Send the
parameter
to the dynamic contractdync’s `entry_point
.
We can use.run(…)
on the generated call as described in Registering and Displaying Calls to Entry Points.
Checking Assertions
To verify conditions, we use scenario.verify
. To verify an equality condition, we can also use scenario.verify_equal
which works on both comparable and non-comparable types.
scenario.verify(c1.data.myParameter == 51)
scenario.verify_equal(c1.data.myList, [2, 3, 5, 7])
Interactive Testing
To test interactively a contract, we use scenario.simulation
.
It also provides a step-by-step mode that is very usefull to understand some computation.
scenario.simulation(c1)
Cryptography in Test Scenarios
Some constructions are only available in tests, not in smart contracts.
sp.test_account(seed)
-
The class
alice = sp.test_account("Alice")
Create a deterministic key-pair from a “seed” string.-
alice.address
Get the public-key-hash as aTAddress
. -
alice.public_key_hash
Get the public-key-hash as aTKeyHash
. -
alice.public_key
Get the full public-key as aTKey
. -
alice.secret_key
Get the secret-key as aTString
.
-
sp.make_signature(secret_key, message, message_format = 'Raw')
-
See Signatures.
sp.test_account
methods and sp.make_signature
are not available for compilation to
Michelson (a smart contract cannot manipulate secret keys).
Test without Explicit Scenarios
sp.add_simulation_target(contract, name="Simulation", shortname=None, profile=False, is_default=True)
-
As a convenience, one can call
sp.add_simulation_target(contract, …)
instead of@sp.add_test …
.
sp.add_simulation_target
optional parameters are: name="Simulation"
-
Optional parameter with default value =
"Simulation"
. shortname=None
-
Optional parameter. Short names need to be unique. Used in smartpy-cli outputs.
profile=False
-
Computes and pretty-prints profiling data.
is_default=True
-
Determines if the test is performed by default when evaluating all tests. Can be typically used in conjonction with
sp.in_browser
in templates to improve speed in browser.
See reference FA2 template.
Compilation Targets
Adding a Compilation Target
We can also add compilation targets by doing
sp.add_compilation_target(name, contract, storage=None)
-
Define a
contract
withname
with optionalstorage
.
Compilation targets also use scenarios behind the scene. sp.add_expression_compilation_target(name, expression)
-
Define an
expression
calledname
.
Compilation targets also use scenarios behind the scene.
Compilation Target Examples
# A contract with an empty (unit) storage
sp.add_compilation_target("min_comp", MyContract())
# A contract with a simple int storage
sp.add_compilation_target("min_comp_int", MyContract(x = 1))
# An expression
sp.add_expression_compilation_target("x", 42)
# Another expression
sp.add_expression_compilation_target("y", ("a", [1, 2, 3]))
Scenario-based Custom Targets
Custom targets
A custom target is similar to a test or a compilation target but associated with
another custom kind
(a free form string).
@sp.add_target(name, kind)
-
Introduce a custom target.
@sp.add_target(name = "orig", kind = "origination")
def origin():
scenario = sp.test_scenario()
c1 = MyContract(x=12)
scenario += c1
Command Line Interface
Installation
See installation instructions.
Check version
This command tells which CLI version is installed.
~/smartpy-cli/SmartPy.sh --version
Dependencies
smartpy-cli depends on python3
and node.js
.
Execution
Executing a SmartPy Script with its tests
- SmartPy.sh test
-
Perform tests defined in a
script.py
(see Tests and Scenarios).
~/smartpy-cli/SmartPy.sh test <script.py> <output-directory>
This includes many outputs: types, generated michelson code, pretty-printed scenario, etc.
Compiling SmartPy Contracts or Expressions
- SmartPy.sh compile
-
Compute the Compilation Targets defined in a
script.py
.
Warning: the interface changed.
~/smartpy-cli/SmartPy.sh compile <script.py> <output-directory>
Example:
~/smartpy-cli/SmartPy.sh compile welcome.py /tmp/welcome
Deploying a contract
# Using Micheline format (.tz)
~/smartpy-cli/SmartPy.sh originate-contract --code code.tz --storage storage.tz --rpc https://florencenet.smartpy.io
# Using Michelson format (.json)
~/smartpy-cli/SmartPy.sh originate-contract --code code.json --storage storage.json --rpc https://florencenet.smartpy.io
# By default, the originator will use a faucet account.
# But you can provide your own private key as an argument
~/smartpy-cli/SmartPy.sh originate-contract --code code.json --storage storage.json --rpc https://florencenet.smartpy.io --private-key edsk...
Custom Targets
- SmartPy.sh kind <kind>
-
Similar to tests. Perform scenarios defined in a
script.py
introduced by the custom target.
~/smartpy-cli/SmartPy.sh kind <kind> <script.py> <output-directory>
CLI optional arguments
SmartPy.sh
takes several optional arguments.
--purge
-
Empty the output directory before writting to it.
--html
-
Add some
.html
outputs such as alog.html
which mimicks the output panel. --protocol <delphi|edo|florence|granada>
-
Select a Tezos protocol.
Default isflorence
. --<flag> arguments
-
Set some
<flag>
witharguments
. --<flag>
-
Activate some boolean
<flag>
. --no-<flag>
-
Deactivate some boolean
<flag>
. --mockup
-
Run in mockup (experimental, needs installed source).
--sandbox
-
Run in sandbox (experimental, needs installed source).