Starting with SmartPy Part 1: Smart Contract Crash Course
Starting Your First Smart Contract with SmartPy
SmartPy is a high-level smart contract library in Python that offers a simple, intuitive and powerful syntax, enabling developers to easily define Michelson smart contracts for the Tezos blockchain. There is an online editor to quickly begin exploring SmartPy. The following is a brief, hands-on tutorial to help you get started developing Tezos smart contracts with SmartPy.
Fundamental Components
Let’s begin by creating a simple smart contract that allows users to store a single phrase string
on the blockchain.
First, we must import the SmartPy library in order to use its functions.
import smartpy as sp
We define a Python class PhraseKeeper
that inherits from the Contract
class of SmartPy. This class will represent our smart contract and enclose all of the storage initialization, entry points, and internal functions.
class PhraseKeeper(sp.Contract):
Next, we define an initialization method for PhraseKeeper
that determines the initial storage during contract origination. __init__()
accepts one argument, initialPhrase
, and calls self.init()
to assign its value to the newly-created phrase
field of type string
in the contract’s storage.
def __init__(self, initialPhrase):
self.init(phrase = sp.string(initialPhrase))
Then, we can start defining the contract’s behavior through the use of entry points, which serve a similar role to public functions for smart contracts. Our first entry point, setPhrase()
, accepts two arguments, self
and params
. All SmartPy entry points require this exact parameter signature and a @sp.entryPoint
decorator to denote it during compilation.
self
is a reference to the current instance of the smart contract PhraseKeeper
that is always the first argument of every entry point. params
can contain multiple fields representing the actual arguments, but in this contract there is only one. setPhrase()
takes the value of params
and assigns it to the phrase
field in self.data
, which points to PhraseKeeper
’s storage.
@sp.entryPoint
def setPhrase(self, params):
self.data.phrase = params
Congratulations! We’ve now successfully created a simple SmartPy contract that initializes its storage, consisting of one string
, with the value passed during contract deployment. It also provides users with an entry point to update the stored string
with another string
.
import smartpy as spclass PhraseKeeper(sp.Contract):
def __init__(self, initialPhrase):
self.init(phrase = initialPhrase) @sp.entryPoint
def setPhrase(self, params):
self.data.phrase = params
Basic Tests
The SmartPy online editor isn’t just a simple text editor for smart contracts — it comes with a built-in simulation suite that provides powerful testing tools for developers. To access detailed information and analytics about our new PhraseKeeper
contract, we can quickly define a basic test.
First, we define a SmartPy test with the annotation @addTest(name = "PhraseSetTest")
and the method header def test()
. Then, PhraseKeeper("Hello World")
creates an instance of the PhraseKeeper
contract with "Hello World"
as the initial phrase
in storage, as per our __init__()
method. We transform the contract into a viewable format with c1.fullHtml()
, and then send the HTML results to the SmartPy output panel with setOutput(html)
.
@addTest(name = "PhraseSetTest")
def test():
scenario = sp.testScenario()
scenario.h1("String Test")
c1 = PhraseKeeper("Tezos Tacos Nachos")
scenario += c1 scenario.h2("Update text")
scenario += c1.setPhrase("Yum Yum Yum")
After pressing the Run
button on the command bar at the top of the editor interface, information about the PhraseKeeper
contract should appear on the right panel as pictured below.
While this is a rudimentary test that shows general information about the contract, it is also possible to define more advanced tests that can invoke the smart contract in a local simulator and verify its expected behavior. Please refer to Part 3 for a more in-depth dive into SmartPy’s testing suite.
Multiple Parameters
We can quickly revise our original smart contract and create a SimpleMath
contract that is capable of performing on-chain computations. To begin, we change __init__()
to create a sum
field of type int
in storage and set its value to 0
. For this contract, hard-coding the starting value makes sense rather than allowing user initialization like in the first example.
import smartpy as sp
class SimpleMath(sp.Contract):
def __init__(self):
self.init(sum = sp.int(0))
SmartPy can infer some datatypes, so the explicit definition of sum can be shortened to sum = 0
. Then, we define a computeSum()
as an entry point that adds the values of params.augend
and params.addend
, before assigning it to self.data.sum
. Entry points are functions that are callable via contract invocation.
Note that even though computeSum()
requires multiple arguments, the entry point header only lists self
and params
as parameters, without explicitly declaring augend
and addend
as int
fields within params
. This unusual approach conforms to Michelson’s single parameter restriction.
@sp.entryPoint
def computeSum(self, params):
self.data.sum = params.augend + params.addend
Luckily, SmartPy will do most of the heavy lifting in the background for us. It will automatically recognize that params
is required to contain two int
values, which it will wrap in a pair
structure when compiling into Michelson, like so: (pair (int %addend) (int %augend))
.
import smartpy as spclass SimpleMath(sp.Contract):
def __init__(self):
self.init(sum = 0) @sp.entryPoint
def computeSum(self, params):
self.data.sum = params.augend + params.addend@addTest(name = "MathTest")
def test():
scenario = sp.testScenario()
scenario.h1("Simple Math Tests") contract = SimpleMath() scenario += contract
scenario.h2("Test Addition")
scenario += contract.computeSum(augend = 1, addend = 2).run(sender = sp.address("tz1234"))
Here’s the contract in the SmartPy online editor.
It’s worth noting that Python is perhaps an odd choice as a smart contract interface for Tezos since one of the core premises of the platform is formal verification which depends on explicit type definitions — a feature Python explicitly avoids. Python however does have easy to understand syntax and is a well-known language. We’re excited for this reason to be working with such user-friendly tools.
With these minor modifications, we now have the knowledge to define complex entry points with multiple parameters. Read Part 2 to learn how to enhance your skills and write more advanced smart contracts.