Michelson must be one of the most exciting programming languages for smart contracts at the moment. It’s a stack-based, strictly typed language in which smart contracts are written to ensure the safety of the Tezos blockchain. Michelson is comparable to the bytecode of the Ethereum smart contracts but it’s more readable, safer, and more robust. All the high-level languages you can use to write smart contracts for Tezos — like SmartPy, Ligo, or Lorentz — eventually compile down to Michelson.
In this first article, we will dip our toes into Michelson language, understand what “stack-based” means and write some very simple smart contracts. This article is mainly written for beginners in programming and/or Tezos development, but intermediate programmers who want to know more about Michelson will also find useful information here. We are going to use the Jupyter kernel developed by Baking Bad to write Michelson code in the Jupyter notebook. You will find a link in each section if you want to see the code at work.
Let’s write some code!
To understand how Michelson works, one of the main concepts to understand properly is the stack. Every Michelson contract is a list of instructions that follow each other. These instructions are in a precise order and are executed in the order they’re written in.
Every instruction will manipulate the stack in some way. Imagine it as a pile of data. The instructions you write will cause an effect on the data present in the pile. You can, for example, add together two pieces of data on top of the pile, remove the one on the top, put another piece of data on top, transfer some tokens, etc. The stack works on a last in, first out basis: if you want to access a piece of data that is not at the top of the stack, you must first deal with the ones above it.
There are three main concepts you must remember when coding in Michelson:
Let’s look at an example.
If you want to add a piece of data on top of the stack, you will call the PUSH operation. This is how it works:
Note that there may already be data in the stack, in which case the new value will be put on top of them. This is how you push new data in Michelson:
PUSH value-type value
For example, if you want to push an integer, you will write
PUSH int 2
, for a string, you will write PUSH string "Tezos"
.A smart contract in Michelson displays a simple structure made of three components:
This translates to the following code:
parameter parameter-type ;
storage storage-type ;
code {
...
}
In addition to this structure, there are two rules you must keep in mind when writing a smart contract in Michelson:
(pair parameter storage)
is always automatically pushed to the stack when the code is executed. Remember — if there’s no parameter, Unit
is used instead.(pair list(operation) storage)
. The execution will stop when this kind of pair is the last thing remaining in the stack.Now that we know about PUSH and the structure of a smart contract in Michelson, let’s write one!
For this contract, we are going to write a “Hello world” contract and save a string into the storage:
Here’s what happens when this code is executed:
parameter unit
indicates that the passed parameter is of type unit
(basically, no parameter).storage string
indicates that the contract has storage of type string
.DROP
is an operation code that removes whatever is at the top of the stack. Remember, we said earlier that a pair with the parameter and the storage is automatically included on top of the stack at the beginning, we are not going to use it, we can just drop it.PUSH
brings a value on top of the stack, here the string “Hello world”.NIL
is an opcode that adds an empty list of the specified type (operation
here) on top of the stack.PAIR
takes the two elements on top of the stack, creates a new pair containing these two elements, and pushes back the pair on the stack.Note: Every instruction ends with a semi-colon (it is optional for the last instruction though).
Let’s introduce a new operation:
ADD
. You probably guessed what it does — add two numerical values together.Here’s a simple contract that demonstrates how it works:
Let’s go through each operation to understand what is happening inside the stack:
parameter unit
: Once again, we are not using any parameter, so we are passing a unit.storage int
: This time, we are saving a value of type integer into the storage.DROP
: We don’t need the initial pair, so we can get rid of it to make room for the values we will actually need.PUSH int 2 ; PUSH int 3 ;
: Note that the order is crucial. int 2
is going to be at the bottom of the stack once you push int 3
. In the case of an addition, the order doesn’t matter too much, but if you subtract two numbers, for example, it’s essential to push them in the right order.ADD
works on the same principle as PAIR
. You take the first two elements on top of the stack and get a single value out of them that you push back to the stack. ADD
will add two numbers together. Note that the numbers have to be both of the same numerical type (you cannot, for example, add an integer and a nat together).NIL
: As in the previous contract, we push an empty list of operations.PAIR
: Creates the pair containing the list of operations and the new storage we need to stop the execution of the contract.The complexity of the Michelson language is often overestimated. That’s probably due to the fact that there are no beginner-friendly tutorials out there and the rare documentation available online is extremely technical and difficult to read for neophytes. This’s why I decided to go through the process of learning Michelson myself, using the difficult documentation to create a series of tutorials that I hope are more accessible.
Understanding Michelson is key to understanding and appreciating the uniqueness of the Tezos blockchain and what makes it more secure and more useful.
In the next part, we will continue diving into Michelson. We’ll write some simple smart contracts and explore the amazing Jupyter notebooks created by the Baking Bad team that will allow us to write Michelson code and understand exactly what’s going on.
Stay tuned!