B9lab Logo
Tezos Developer Portal
Developer PortalDeveloper Portal

SmartPy - First Steps

A first glance


Using a popular programming language, such as Python, to write smart contracts is an immense advantage for first-time smart contract programming. Most smart contracts are written in protocol- or smart contract-specific languages.

With SmartPy you can dive into smart contract writing without the necessity to learn a smart contract-specific language before getting your hands on any smart contract development. So, it makes life much easier.

SmartPy offers different tools:

  • Analytics: Elements of the UI in SmartPy.io to analyse smart contracts and their properties;
  • SmartML: A virtual machine written in OCaml;
  • Compiler: Used to translate contracts from SmartML to Michelson;
  • Python library: Facilitates the use of SmartML definitions.
info icon

We will address the different tools throughout this section.

We will mainly deal with the Python library of SmartPy.

The SmartPy library is used to access SmartML definitions; We can get a SmartML piece from a SmartPy piece. SmartPy offers a compiler to translate SmartML to Michelson.

SmartPy - Smart ML - Michelson
SmartPy - Smart ML - Michelson

SmartPy Editor

We can work with an online editor that gives us an integrated development environment.

You can access the online editor by going to: https://smartpy.io

editor1a

where you can go to the IDE, or access information regarding the SmartPy CLI, articles and guides, blog posts, etc..

Or go directly to the IDE at https://smartpy.io/ide.

editor1

In the IDE, you can find a number of helpful tabs in the upper left corner.

editor1b

Templates helps you load one of the available templates.

A very nice feature is Share, because you can share your embedded code with a link or with IPFS:

editor1d

You can find tabs to redirect you to the SmartPy Contract Explorer, SmartPy Wallet, and the editor for Michelson.

editor1f

The SmartPy Explorer, a very helpful tool, looks like this (we will use it soon):

explorergen

Let's begin with a simple program to gather some experience with this editor:

import smartpy as sp

@sp.add_test(name = "First test")
def test():
    scenario = sp.test_scenario()
    scenario.p("First Output")

We use a decorator to add a test. This type of tests can be conducted with the online editor.

editor2

In standard view, we can display HTML on the right-side half:

import smartpy as sp

@sp.add_test(name = "First test")
def test():
    scenario = sp.test_scenario()
    scenario.p("<h1>First Output</h1>")

SmartPy has also other methods to help with documentation:

scenario.h1("a title")
scenario.h2("a subtitle")
scenario.h3(..)
scenario.h4(..)
scenario.p("Some text")

We can create as many test outputs as we want:

import smartpy as sp

@sp.add_test(name = "First_test")
def test():
    scenario = sp.test_scenario()
    scenario.p("First Output")

@sp.add_test(name = "Second_test")
def test():
    scenario = sp.test_scenario()
    scenario.p("Second Output")

You can switch between the tests using the Tests option in the menu bar.

editor tests

Repeater contract

Let's again start with a repeater contract.

A repeater contract waits for an input, and then gives it back as an output without editing. Remember, each contract takes as input one pair of a parameter and storage structure, and then returns as output one pair consisting of an operation list and another storage structure.

So, we will save the input parameter simply in the storage:

# Import SmartPy
import smartpy as sp

# Define Smartcontract

class Repeater(sp.Contract):
    def __init__(self):
        # Define a value with initial integer 0
        self.init(storage=0)

    # Define Entry Point
    @sp.entry_point
    def repeat(self, params):
        self.data.storage= params

@sp.add_test(name = "First_test")
def test():
    first_contract= Repeater()
    scenario = sp.test_scenario()

    scenario.register(first_contract, show = True)
    scenario+= first_contract.repeat(2)

We want to take a closer look at this script.

As you can see, with

import smartpy as sp

we can import SmartPy like a common Python module.

class Repeater(sp.Contract):
    def __init__(self):
        # Define a value with initial integer 0
        self.init(storage=0)

Please don't let you get confused at this point. We consciously choose the name storage for the number that we save. Later on you can see that we can produce considerably more complicated data structures. SmartPy itself offers us self.data, which corresponds to the storage in Michelson. Our class Repeater inherits the class sp.Contract.

    # Define Entry Point
    @sp.entry_point
    def repeat(self, params):
        self.data.storage= params

Here you can notice that we save the input directly in self.data.storage. We also define a so-called entry point. Previous self.init will iterate on the defined entry points to build the smart contract.

Afterwards, we program the test and the output:

@sp.add_test(name = "First_test")
def test():
    first_contract= Repeater()
    scenario = sp.test_scenario()

    scenario.register(first_contract, show = True)
    scenario+= first_contract.repeat(2)

This part almost looks like a normal Python program. This is the strength of SmartPy: We can use Python for metaprogramming.

editor3

We use register(first_contract, show = True) to produce the first output. For this reason, we have access among other to the Michelson code.

Have a look at the Michelson code and the test transaction:

editor tx

There you see our repeater contract, which we already coded and tested in Michelson. Michelson does not have variable names, hence the compiler comments with the help of our source code.

tip icon

Before you continue, try to change the repeater contract so that a string is given as input. Take a look at the Michelson code: What changed? Examine the TYPES tab of the output.

Multiple parameters

How do we manage multiple inputs?

Let's extend our repeater, and code an adder:

import smartpy as sp

# Define Smartcontract

class Adder(sp.Contract):
    def __init__(self):
        # Define storage with initial integer 0
        self.init(storage=0)

    # Define Entry Point
    @sp.entry_point
    def add(self, params):
        self.data.storage= params.first + params.second

@sp.add_test(name = "First_test")
def test():
    adder_contract= Adder()

    scenario = sp.test_scenario()
    scenario+=adder_contract

    scenario+= adder_contract.add(first=2, second=3)

You see that you can use multiple inputs:

adder_contract.add(first=2, second=3)

and access these like:

self.data.storage= params.first + params.second
reading icon
Discuss on Slack