# Chapter 14

All the use cases we have explored so far follow a very simple flow: a transaction hits the smart contract, the parameter sent with the transaction and the storage are pushed as a pair onto the stack, the code of the contract manipulates the stack without or without the help of environment variables and a pair containing a list of transactions (that was until now empty) and the new storage is returned.

However, smart contracts on the Tezos blockchain are also able to interact with the blockchain itself. They can create and send new transactions, they can delegate their funds to bakers and they can even originate new contracts! This will be the subject of this chapter.

The interactions between your smart contract and the blockchain allow for the creation of rich and complex applications that can run multiple actions in the background while keeping the requirement for action from your users minimal. Let's start with one of the most important ones, the creation of new transactions!

# Creating and sending transactions

Until now, we have always returned an empty list of transactions at the end of our smart contracts. A list of transactions, may it be empty or not, is necessary to stop the execution of a smart contract. At the end of the contract, there are two kinds of transactions you may want to send to the network: an amount of tokens to send to an implicit account or a transaction to another smart contract.
It is important to remember that whatever happens inside the contract code has no effect on the blockchain. Nothing will be changed in the smart contract or the rest of blockchain before the contract returns a list of operations and its new storage. It may seem like a limitation, for example, it makes it more difficult to use data from another smart contract inside your contract, but it is actually a necessary security feature: this self-contained environment guarantees that no intervention from outside the contract can modify its storage. Your code modifies the storage without any outside influence in a very deterministic way.

That being say, it is of course possible to interact with the Tezos blockchain from the smart contract, at the end of the execution. One of the most common interactions is sending a transactions. If you write a contract that holds people's funds, there should be a way to send them back to them! You could also write a contract that fetches some data from another contract (for example an oracle) and needs to send a transaction.

Let's check first how sending a transaction to an implicit account looks like:

PATCH SENDER "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" ;
PATCH BALANCE 200 ;

parameter mutez ;
storage unit ;
code {
    CAR ;
    DUP ;
    BALANCE ;
    IFCMPLT
        { FAIL }
        {
            SENDER ;
            CONTRACT unit ;
            IF_NONE
                { FAIL }
                {
                    SWAP ;
                    UNIT ;
                    TRANSFER_TOKENS ;
                    NIL operation ;
                    SWAP ;
                    CONS ;
                    UNIT ;
                    SWAP ;
                    PAIR
                } ;
        };
} ;

RUN %default 100 Unit ;
stdout
PATCH: set SENDER=tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb; PATCH: set BALANCE=200; parameter mutez; storage unit; code { CAR ; DUP ; BALANCE ; { { COMPARE ; LT } ; IF { { UNIT ; FAILWITH } } { SENDER ; CONTRACT unit ; IF_NONE { { UNIT ; FAILWITH } } { SWAP ; UNIT ; TRANSFER_TOKENS ; NIL operation ; SWAP ; CONS ; UNIT ; SWAP ; PAIR } } } }; RUN: use %default; drop all; push (100, Unit); CAR: pop (100, Unit); push 100; DUP: push 100; BALANCE: push 200; COMPARE: pop 200, 100; push 1; LT: pop 1; push False; IF: pop False; SENDER: push tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb; CONTRACT: pop tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb; skip check; push ('tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb%default',); IF_NONE: pop ('tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb%default',); push tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb%default; SWAP: pop tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb%default, 100; push tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb%default; push 100; UNIT: push Unit; TRANSFER_TOKENS: pop Unit, 100, tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb%default; set BALANCE=100; push <send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>; NIL: push []; SWAP: pop [], <send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>; push []; push <send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>; CONS: pop <send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>, []; push ['<send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>']; UNIT: push Unit; SWAP: pop Unit, ['<send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>']; push Unit; push ['<send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>']; PAIR: pop ['<send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>'], Unit; push (['<send 100 to tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb>'], Unit);
value type
Unit
unit

kind target amount entrypoint parameters
transaction
tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
100
default
Unit

This simple smart contract takes an amount of mutez as a parameter, checks if the balance is greater than the requested amount and sends the amount to the sender of the transaction.

In order to create a transaction, you need three elements in the following order on the stack:

  1. A parameter to send with the value of type contract
  2. An amount in mutez
  3. A value of type contract

This can be a bit confusing because here, we don't want to send a transaction to a contract but to an implicit account. However, implicit accounts can be seen as codeless contract. Because they don't have any code, they also don't accept any parameter or more precisely, they accept a parameter of type unit.

To send a transaction to an implicit account, we will first cast an address into a contract. We can use the CONTRACT instruction with a parameter of type unit (because implicit accounts don't have a parameter). Remember that this instruction returns a value of type option so we have to use IF_NONE/IF_SOME to extract the value. When this is ready, we can add the amount in mutez we want to send (0 is also acceptable). Finally, we have to push a value of type unit on top of the previous two elements, for example with UNIT. Once the stack is ready, we can call TRANFER_TOKENS. Although the name can be misleading, this is the instruction that will create a new transaction. Don't forget that, at this point, we have merely created the transaction and it hasn't been sent yet. The transaction will only be sent after we put it inside a list of elements of type operation and end the execution of the contract.

Now, you understood the basics of sending a transaction from a smart contract, so let's see how we can do the same to send a transaction to another smart contract:

parameter (pair address int) ;
storage unit ;
code {
    CAR ;
    UNPAIR ;
    CONTRACT int ;
    IF_NONE
        { FAIL }
        {
            SWAP ;
            PUSH mutez 0 ;
            SWAP ;
            TRANSFER_TOKENS ;
            NIL operation ;
            SWAP ;
            CONS ;
            UNIT ;
            SWAP ;
            PAIR
        } ;
} ;

RUN %default (Pair "KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3" 4) Unit ;
stdout
parameter (pair address int); storage unit; code { CAR ; { DUP ; CAR ; DIP { CDR } } ; CONTRACT int ; IF_NONE { { UNIT ; FAILWITH } } { SWAP ; PUSH mutez 0 ; SWAP ; TRANSFER_TOKENS ; NIL operation ; SWAP ; CONS ; UNIT ; SWAP ; PAIR } }; RUN: use %default; drop all; push (('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3', 4), Unit); CAR: pop (('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3', 4), Unit); push ('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3', 4); DUP: push ('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3', 4); CAR: pop ('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3', 4); push KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3; DIP: protect 1 item(s); CDR: pop ('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3', 4); push 4; restore 1 item(s); CONTRACT: pop KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3; skip check; push ('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default',); IF_NONE: pop ('KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default',); push KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default; SWAP: pop KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default, 4; push KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default; push 4; PUSH: push 0; SWAP: pop 0, 4; push 0; push 4; TRANSFER_TOKENS: pop 4, 0, KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default; set BALANCE=100; push <call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>; NIL: push []; SWAP: pop [], <call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>; push []; push <call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>; CONS: pop <call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>, []; push ['<call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>']; UNIT: push Unit; SWAP: pop Unit, ['<call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>']; push Unit; push ['<call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>']; PAIR: pop ['<call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>'], Unit; push (['<call KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3%default>'], Unit);
value type
Unit
unit

kind target amount entrypoint parameters
transaction
KT1FM76xKAcN8EQkZVfBmeQzjzwNtpzYVwK3
0
default
4

This transaction is a little more straighforward that the previous one but follows exactly the same step, although with different values. We use an amount of mutez 0 because we don't want to send any token to the contract but we replace the value of type unit with a value of type int. Note that although the address we send is a contract address, Michelson only sees an address and it must be cast to a contract value to process the transaction.

# Delegating contract funds

Like any other address on the Tezos network, contracts can delegate the funds they hold to a baker. This is very simple to implement and creates a new operation to include in the returned list of operations:

parameter key_hash ;
storage unit ;
code {
    CAR ;
    SOME ;
    SET_DELEGATE ;
    NIL operation ;
    SWAP ;
    CONS ;
    UNIT ;
    SWAP ;
    PAIR
} ;

RUN %default "edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn" Unit ;
stdout
parameter key_hash; storage unit; code { CAR ; SOME ; SET_DELEGATE ; NIL operation ; SWAP ; CONS ; UNIT ; SWAP ; PAIR }; RUN: use %default; drop all; push ('edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn', Unit); CAR: pop ('edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn', Unit); push edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn; SOME: pop edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn; push ('edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn',); SET_DELEGATE: pop ('edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn',); push <delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>; NIL: push []; SWAP: pop [], <delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>; push []; push <delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>; CONS: pop <delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>, []; push ['<delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>']; UNIT: push Unit; SWAP: pop Unit, ['<delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>']; push Unit; push ['<delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>']; PAIR: pop ['<delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>'], Unit; push (['<delegate to edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn>'], Unit);
value type
Unit
unit

kind target
delegation
edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn

The SET_DELEGATE instruction must be called with a key_hash wrapped in an option on the top of the stack. The key hash is what is generally called the "public key" (which is different from the "public key hash").
Even if the contract in the example seems to work, be aware that this operation will fail if the address you use is not a registered delegate (meaning that you cannot use any address as this is the case in this example). The operation also fails if you send the address to which the funds are already delegated. It is also possible to use None before calling SET_DELEGATE if you want to remove the current delegate.

# Originating new contracts

Contracts can even create new contracts or "originate" them in the Tezos lingo. Contracts that can originate other contracts on demand are called "contract factories". Before creating a new contract, the stack must present the following elements in the given order:

  1. The delegate address wrapped in an option type (or None)
  2. The initial balance you want to give to the new contract from the current contract (or 0)
  3. The code of the new contract

When these elements are in place on the stack, you can call CREATE_CONTRACT to originate the new contract as illustrated in the example below:

parameter unit ;
storage unit ;
code {
    DROP ;
    PUSH int 5 ;
    PUSH mutez 0 ;
    PUSH key_hash "edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn" ;
    SOME ;
    CREATE_CONTRACT { int ; int ; { UNPAIR ; ADD ; NIL operation ; PAIR } } ;
    NIL operation ;
    SWAP ;
    CONS ;
    DIP { DROP } ;
    UNIT ;
    SWAP ;
    PAIR
} ;

RUN %default Unit Unit ;
stdout
parameter unit; storage unit; code { DROP ; PUSH int 5 ; PUSH mutez 0 ; PUSH key_hash "edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn" ; SOME ; CREATE_CONTRACT { int ; int ; { { DUP ; CAR ; DIP { CDR } } ; ADD ; NIL operation ; PAIR } } ; NIL operation ; SWAP ; CONS ; DIP { DROP } ; UNIT ; SWAP ; PAIR }; RUN: use %default; drop all; push (Unit, Unit); DROP: pop (Unit, Unit); PUSH: push 5; PUSH: push 0; PUSH: push edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn; SOME: pop edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn; push ('edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn',); CREATE_CONTRACT: pop ('edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn',), 0, 5; set BALANCE=100; push KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW; push <originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>; NIL: push []; SWAP: pop [], <originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>; push []; push <originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>; CONS: pop <originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>, []; push ['<originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>']; DIP: protect 1 item(s); DROP: pop KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW; restore 1 item(s); UNIT: push Unit; SWAP: pop Unit, ['<originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>']; push Unit; push ['<originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>']; PAIR: pop ['<originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>'], Unit; push (['<originate KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW>'], Unit);
value type
Unit
unit

kind target amount storage code delegate
origination
KT1Hyjg9rScZad4FpZCTg2zgXYHgyULq9hTW
0
5
{ { DUP ; CAR ; DIP { CDR } } ; ADD ; NIL operation ; PAIR }
edpkvGfYw3LyB1UcCahKQk4rF2tvbMUk8GFiTuMjL75uGXrpvKXhjn

As you can tell from the contract, once all the arguments are in the correct position in the stack, you can call the CREATE_CONTRACT instruction which takes one argument: the code of the contract it will originate. Between curly braces, you must start with the type of the storage, before the type of the parameter and finally the code of the contract itself. CREATE_CONTRACT returns a new operation that you must include in the list of operations to be executed at the end of the current contract and the address of the newly created contract.

add add