Michelson - A Smart Contract Language
A general overview
We already talked about Michelson, and you have come in contact with it when you took a look at the compiled code. But we did not dive deeper, and just wrote a simple repeater contract.
Before you dive deeper, please make sure to read the previous section on Michelson.
Here, we want to give a general overview. In the end, we want to write a pure Michelson code to have a more complex example of the language.
Core data types
| Data Type | Description | 
|---|---|
string | For strings | 
nat | For natural numbers | 
int | For integers | 
bytes | For bytes | 
bool | For booleans, can be True or False | 
unit | Placeholder with value Unit | 
list (t) | An immutable list of type (t) | 
pair (l) (r) | A pair of two values of type (l) and (r) | 
option (t) | Optional value of type (t) | 
or (l) (r) | A value holding a value of type (l) or (r) | 
set (t) | An immutable set of type (t) | 
map (k) (t) | An immutable map of type (k) to type (t) | 
big_map (k) (t) | Lazily deserialized maps, limited use. | 
Notice, that types are lowercase in Michelson. A data constructor is capitalized(e.g. True or False) and instructions are uppercase. 
Do you want to take a closer look at core data types and notations, check this out: https://tezos.gitlab.io/008/michelson.html#core-data-types-and-notations.
Domain-specific data types
| Data Type | Description | 
|---|---|
operation | An internal operation emitted by a contract | 
key | A public cryptography key | 
key_hash | The hash of a public cryptography key | 
address | An untyped contract address | 
timestamp | Dates in real world | 
mutez | 64 bit signed integers | 
contract t | A contract | 
signature | A cryptographic signature | 
lambda t u | A lambda with arguments of type t and return type of u | 
For a closer look at domain-specific data types, go here.
Stack operations
| Operation | Description | 
|---|---|
DROP | Drop the top element of the stack | 
DUP | Duplicate the top element of the stack | 
SWAP | Exchange the top two elements of the stack | 
PUSH t x | Push a value of x of type t onto the stack | 
UNIT | Push a unit value onto the stack | 
LAMBDA t u | Push a lambda onto the stack | 
CAR | Push the left part of a pair onto stack | 
CDR | Push the right part of a pair onto stack | 
NIL (t) | Push an empty list of type ((list (t)) | 
For a more detailed account on stack operations, we recommend https://tezos.gitlab.io/008/michelson.html#stack-operations.
Comparison
The COMPARE instruction works for each comparable type. The result will be 0 if the top two elements of the stack are equal, -1 if the first element in the stack is less than the second, and +1 otherwise.
| Operation | Description | 
|---|---|
EQ | Checks if the top element of the stack is 0 | 
NEQ | Checks if the top element of the stack is not 0 | 
LT | Checks if the top element of the stack is negative | 
GT | Checks if the top element of the stack is positive | 
LE | Checks if the top element of the stack is negative or 0 | 
GE | Checks if the top element of the stack is positive or 0 | 
For more information on generic comparison: https://tezos.gitlab.io/008/michelson.html#generic-comparison.
In addition, there are syntactic sugars for COMPARE.
Operations on strings
| Operation | Description | 
|---|---|
CONCAT | String concatenation | 
SIZE | number of characters in a string | 
SLICE | String access | 
COMPARE | Lexicographic comparison | 
More on operations on strings? Check out: https://tezos.gitlab.io/008/michelson.html#operations-on-strings.
A smart contract to certify students
Let us write a smart contract using this knowledge. We want to rewrite our certification smart contract with Michelson.
Create a new file certify.tz in the same folder as hangzhounet.sh and paste:
parameter address;
storage (map address bool);
code { DUP;
       CDR @certified;
       SWAP;
       CAR @student; 
       DIP { PUSH bool True; SOME; };
       UPDATE;
       NIL operation;
       PAIR };You must understand the stack. Working with Michelson means working with a stack. What do we do in this part of the code? Let us take a look at the stack:
$ ./hangzhounet.sh client typecheck script container:certify.tz -detailsThis should give you the output:
{ parameter address ;
  storage (map address bool) ;
  code { /* [ pair (address @parameter) (map @storage address bool) ] */
         DUP
         /* [ pair (address @parameter) (map @storage address bool)
            : pair (address @parameter) (map @storage address bool) ] */ ;
         CDR @certified
         /* [ @certified map address bool
            : pair (address @parameter) (map @storage address bool) ] */ ;
         SWAP
         /* [ pair (address @parameter) (map @storage address bool)
            : @certified map address bool ] */ ;
         CAR @student
         /* [ @student address : @certified map address bool ] */ ;
         DIP { /* [ @certified map address bool ] */
               PUSH bool True
               /* [ bool : @certified map address bool ] */ ;
               SOME
               /* [ option bool : @certified map address bool ] */ }
         /* [ @student address : option bool : @certified map address bool ] */ ;
         UPDATE
         /* [ @certified map address bool ] */ ;
         NIL operation
         /* [ list operation : @certified map address bool ] */ ;
         PAIR
         /* [ pair (list operation) (map @certified address bool) ] */ } }First, let us understand the syntax of the output. We see:
  code { /* [ pair (address @parameter) (map @storage address bool) ] */This means if the program starts, the stack will have one element. The type of this element is pair (address @parameter) (map @storage address bool).
You can see that the output indicates the @parameter and @storage. In the next step we get the map of the certified students with:
DUP;
CDR @certified;Here you see that we use @certified so the output tells us:
@certified map address boolThis is very useful for understanding the stack. We want to have a map address bool to store students' certification status.
Now we can take the address of the student which we want to certify:
SWAP;
CAR @student; The result is:
/* [ @student address : @certified map address bool ] */ ;So we have two elements in the stack, an element of type address and an element of type map address bool.
Now we want to update the map:
DIP { PUSH bool True; SOME; };
UPDATE;DIP will keep the top of the stack so we can push a type of option bool between the student address and map to get the stack:
[ @student address : option bool : @certified map address bool ]This is because of the UPDATE instruction. You can find it in the Michelson documentation:
UPDATE / x : Some y : {} : S  =>  { Elt x y } : S
UPDATE / x : Some y : { Elt k v ; <tl> } : S  =>  { Elt k y ; <tl> } : SSo this way we add a new student with the address and True to indicate that this student is certified. Now we need to match the type of the output:
NIL operation;
PAIR };Let us run test smart contract:
$ ./hangzhounet.sh client run script container:certify.tz on storage '{}' and input '"tz1Q2zkgZENNF2g95NN7g1CtxAqKynWViSeN"' --trace-stackYou can also deploy the contract and interact with it further. But as you can see, this contract will not check who is issuing a certificate.
Let us change this:
parameter address;
storage (pair (map address bool) address);
code { DUP;
       CDR;
       CDR @certifier;
       DUP;
       SENDER;
       COMPARE;
       NEQ ;
       IF { PUSH string "Only certifier is allowed to certify" ; FAILWITH }
          { UNIT } ;
       DROP;
       SWAP;
       DUP;
       CDR;
       CAR @certified;
       SWAP;
       CAR @student; 
       DIP { PUSH bool True; SOME; };
       UPDATE;
       PAIR;
       
       NIL operation;
       PAIR };This time we have:
CDR;
CDR @certifier;
DUP;
SENDER;
COMPARE;
NEQ ;
IF { PUSH string "Only certifier is allowed to certify" ; FAILWITH }
  { UNIT } ;at the beginning. SENDER will give us the address of the contract which is calling our contract. We can COMPARE it with the certifier in the storage. It will push a zero only if those addresses match. We check this with NEQ and if it is not zero, we will abort the transaction with FAILWITH. 
Let us do a typecheck:
$ ./hangzhounet.sh client typecheck script container:certify.tz -detailsThe output will be:
{ parameter address ;
  storage (pair (map address bool) address) ;
  code { /* [ pair (address @parameter) (pair @storage (map address bool) address) ] */
         DUP
         /* [ pair (address @parameter) (pair @storage (map address bool) address)
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         CDR
         /* [ @storage pair (map address bool) address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         CDR @certifier
         /* [ @certifier address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         DUP
         /* [ @certifier address : @certifier address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         SENDER
         /* [ @sender address : @certifier address : @certifier address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         COMPARE
         /* [ int : @certifier address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         NEQ
         /* [ bool : @certifier address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         IF { PUSH string
                   "Only certifier is allowed to certify"
              /* [ string : @certifier address
                 : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
              FAILWITH
              /* [] */ }
            { /* [ @certifier address
                 : pair (address @parameter) (pair @storage (map address bool) address) ] */
              UNIT
              /* [ unit : @certifier address
                 : pair (address @parameter) (pair @storage (map address bool) address) ] */ } ;
         DROP
         /* [ @certifier address
            : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
         SWAP
         /* [ pair (address @parameter) (pair @storage (map address bool) address)
            : @certifier address ] */ ;
         DUP
         /* [ pair (address @parameter) (pair @storage (map address bool) address)
            : pair (address @parameter) (pair @storage (map address bool) address)
            : @certifier address ] */ ;
         CDR
         /* [ @storage pair (map address bool) address
            : pair (address @parameter) (pair @storage (map address bool) address)
            : @certifier address ] */ ;
         CAR @certified
         /* [ @certified map address bool
            : pair (address @parameter) (pair @storage (map address bool) address)
            : @certifier address ] */ ;
         SWAP
         /* [ pair (address @parameter) (pair @storage (map address bool) address)
            : @certified map address bool : @certifier address ] */ ;
         CAR @student
         /* [ @student address : @certified map address bool : @certifier address ] */ ;
         DIP { /* [ @certified map address bool : @certifier address ] */
               PUSH bool True
               /* [ bool : @certified map address bool : @certifier address ] */ ;
               SOME
               /* [ option bool : @certified map address bool : @certifier address ] */ }
         /* [ @student address : option bool : @certified map address bool
            : @certifier address ] */ ;
         UPDATE
         /* [ @certified map address bool : @certifier address ] */ ;
         PAIR
         /* [ pair (map @certified address bool) (address @certifier) ] */ ;
         NIL operation
         /* [ list operation : pair (map @certified address bool) (address @certifier) ] */ ;
         PAIR
         /* [ pair (list operation) (pair (map @certified address bool) (address @certifier)) ] */ } }Have a look at:
NEQ
/* [ bool : @certifier address
  : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
IF { PUSH string
         "Only certifier is allowed to certify"
    /* [ string : @certifier address
       : pair (address @parameter) (pair @storage (map address bool) address) ] */ ;
    FAILWITH
    /* [] */ }
  { /* [ @certifier address
       : pair (address @parameter) (pair @storage (map address bool) address) ] */
    UNIT
    /* [ unit : @certifier address
       : pair (address @parameter) (pair @storage (map address bool) address) ] */ } ;
DROPSo if the result of COMPARE is equal to zero, we will use UNIT to push a placeholder into the stack. Then we will DROP it. We do so because IF needs two arguments.
You can also use {} as the second branch of IF, or put the rest of the code in the second branch, as we will do later.
Now, let us test this contract:
$ ./hangzhounet.sh client run script container:certify.tz on storage 'Pair {} "tz1Q2zkgZENNF2g95NN7g1CtxAqKynWViSeN"' and input '"tz1efDUcgRaD6WcChETnhEqW57JMxRCree8a"' --source tz1Q2zkgZENNF2g95NN7g1CtxAqKynWViSeNAnd let us test it with another --source which is not in the storage 'Pair {} "tz1Q2zkgZENNF2g95NN7g1CtxAqKynWViSeN"':
$ ./hangzhounet.sh client run script container:certify.tz on storage 'Pair {} "tz1Q2zkgZENNF2g95NN7g1CtxAqKynWViSeN"' and input '"tz1efDUcgRaD6WcChETnhEqW57JMxRCree8a"' --source tz1efDUcgRaD6WcChETnhEqW57JMxRCree8aThis time it should fail. We can optimize our code and use the second branch of the IF and a big_map instead of a map: 
parameter address;
storage (pair (big_map address bool) address);
code { DUP;
       CDR;
       CDR @certifier;
       DUP;
       SENDER;
       COMPARE;
       NEQ ;
       IF { PUSH string "Only certifier is allowed to certify" ; FAILWITH }
          { SWAP;
            DUP;
            CDR;
            CAR @certified;
            SWAP;
            CAR @student;
            DIP { PUSH bool True; SOME; };
            UPDATE;
            PAIR;
            NIL operation;
            PAIR 
            };
       };Note that you can only have one big_map in a contract. This limitation of big_map will probably be dropped in the future.
Let us deploy this contract. Therefore, first check the address of your accounts/contracts with:
$ ./hangzhounet.sh client list known addresses
Warning:
  
                 This is NOT the Tezos Mainnet.
  
               Do NOT use your fundraiser keys on this network.
faucetWallet: tz1fM3iHj7qWUrUStFwDuhDmjPeiQCWwBDDZ (unencrypted sk known)
myFirstKey: tz1dJbDNdzM5xFVSKkwt5NVPsdgQmWBQpfng (unencrypted sk known)
Then you can use one of your active accounts:
$ ./hangzhounet.sh client originate contract certify for faucetWallet transferring 0 from faucetWallet running container:certify.tz --init 'Pair {} "tz1fM3iHj7qWUrUStFwDuhDmjPeiQCWwBDDZ"' --burn-cap 0.5You will receive the contract address if it is deployed:
New contract KT1MC1dAuJxNcpNu8KMZP3F5RbjryDJ2dF35 originated.
The operation has only been included 0 blocks ago.You can check its state and script with Better Call Dev.
For a closer look at the full grammar of Michelson, go here.
- Barde, Claude (2020): An Introduction to Michelson: The Scripting Language of Tezos (Part 1)
 - Barde, Claude (2020): An Introduction to Michelson: The Scripting Language of Tezos (Part 2)
 - Barde, Claude (2020): An Introduction to Michelson: The Scripting Language of Tezos (Part 3)
 - camlCase: Michelson Tutorial Series
 - Canou, Benjamin: Michelson presentation
 - Michelson: The Language of Smart Contracts in Tezos
 - Tezos Developer Documentation
 - Tezos: Michelson Reference - Introduction, Types, Instructions