Differences between SmartPy and Python
Although SmartPy is similar to Python in many ways, it has some significant differences. These differences apply to code within SmartPy modules. On the contrary, the test scenario is pure Python. Many of these differences exist because SmartPy is compiled to the Michelson stack-based language, which is very different from Python. Other differences exist because SmartPy is made for writing Tezos smart contracts instead of being a general-purpose programming language.
Code within a test scenario is ordinary Python. Therefore, within a test scenario, you can do things that you can't do in a smart contract, such as import and use Python libraries, call external APIs, and work with private keys and test accounts. For more information about tests, see Test scenarios.
Code within a SmartPy module is subject to these limitations:
Module
You cannot use imported Python modules in SmartPy code. You can import SmartPy code from other .spy
files or import SmartPy modules defined within sp.module
blocks.
Please see modules.
Control structures
Functions, loops, and conditionals have limitations in SmartPy that are not in Python:
Functions
Functions (including entrypoints, views, and auxiliary functions) must end at a single block of code. For example, this code is not valid because it could return from more than one place in the code, even though the return
statements are close to each other:
if a > b:
return a # Error: 'return' in non-terminal position.
return b
Instead, functions must return from a single block of code, as in this example:
if a > b:
return a
else:
return b
Loops
Similarly, you can't use the break
command to end a loop early, as in this example:
x = 3
i = 0
while i < 5:
if i == x:
break # SyntaxError: Not a statement: break
i += 1
Logic
- SmartPy supports the Python
if
andelse
statements, but not theelif
statement. - SmartPy does not support Python exception handling with statements such as
try
andexcept
. - SmartPy does not support some built-in Python functions, such as
type
andbool
.
Logging
To write to STDOUT from SmartPy, use the sp.trace
function.
Variables
SmartPy is limited by the types of variables that Michelson supports and how it uses variables:
Types
SmartPy does not support every data type that Python does. Also, SmartPy data types may not have the same methods that the equivalent Python data types have. See the Data types section for the types that SmartPy supports.
Some SmartPy types behave differently from the equivalent Python types. For example, SmartPy numerical types behave differently when they are divided; see Division.
You must be aware of the types that you use in SmartPy modules versus the types that you use in the Python code of test scenarios. For example, within a SmartPy module, lists that you create have the type sp.list[t]
, where t
is the type of the list elements; see Lists, sets, and maps. Therefore, to add elements to a list, you use the push()
method of the sp.list
type, as in this example:
@sp.entrypoint
def lists(self):
my_list = [1, 2, 3]
my_list.push(sp.int(4))
However, lists that you create in Python code, including test scenarios, are ordinary Python lists. Therefore, to add elements to a list, you use the append()
method, as in this example:
@sp.add_test()
def test():
# Create a test scenario
scenario = sp.test_scenario("A Test", main)
# ...
my_list = [1, 2, 3]
my_list.append(4)
Similarly, to check if an element is in a SmartPy set of type sp.set
, use its contains()
method, not the standard Python in
operator.
Casting
In most cases, you cannot change the type of a variable after you define it. The sp.cast
function does not change the type of a variable; it clarifies the type of a variable for the compiler.
The STDLIB modules provide some traditional casting functions, such as converting between different numerical types.
For more information about casting, see Casting.
Enumerations
To set up an enumeration with SmartPy, use a variant type to create a group of cases. Each value has unit as a value, as in this example:
@sp.module
def main():
status: type = sp.variant(Active=sp.unit, Inactive=sp.unit)
class C(sp.Contract):
def __init__(self):
self.data.status = sp.cast(sp.variant.Active(), status)
self.data.statusMessage = ""
with sp.match(self.data.status):
with sp.case.Active:
self.data.statusMessage = "Running"
with sp.case.Inactive:
self.data.statusMessage = "Not running"
Access and iteration
Michelson variables are stored in a stack, which introduces limitations on accessing and iterating over variables. Here are some of those limitations:
You cannot retrieve or change an arbitrary element in a list or set with brackets, as in the code
myList[2]
.You can add items to lists but you cannot remove them without iterating over the list.