Getting started with Tezos #2: contracts

October 27, 2017
easy cryptocurrency tezos michelson blockchain smart contracts

July 30th 2018: Tezos betanet has now launched, and this article doesn’t reflect the current state of the project. You can still read through it, but the code examples won’t work as is, and will need various changes. Up to date documentation for the betanet is available here. You can change the address to access docs for other branches like zeronet.

A word about different testnets: there is usually a zeronet running, which follows new developments closely and an alphanet which is more stable. The real network is currently called betanet (transactions persist to the main network). The alphanet is currently obsolete.

./alphanet.sh client man | grep originate -B 2 -A 2

The B and A flags stand for before and after respectively, and let us see a number of lines surrounding the target phrase when using grep.

We’d like to originate a contract, so these are the lines we care about:

originate contract (new) for (mgr) transferring (qty) from (src) running (prg) [-fee _]
  [-delegate _] [-force] [-delegatable] [-non-spendable] [-init _]

We can see that the command requires us to specify a few parameters. We should specify a new alias for the originated contract in place of (new), an alias or address of an existing manager for (mgr), the starting balance for (qty) and the source of those funds for (src). Last but not least, the file containing the contract code for (prg).

For some contract in a file ourContract.tz it may look like this:

./alphanet.sh client originate contract ourAlias for my_identity transferring 5 from money running container:ourContract.tz

A manager of a contract is usually the owner – in the default case they are able to spend the money contained in the contract (this can be configured when creating the contract). Since we are just creating a simple contract for ourselves, we can go ahead and specify ourselves as the manager. We can use the default identity my_identity. If everything works, after executing the command we should have a new contract published on the blockchain and its address saved under the ourAlias alias. We specified the starting balance of 5 tez, so there will be 5 tez less in our account with the alias money and there will be 5 tez in the newly originated contract.

We can check this:

./alphanet.sh client get balance for ourAlias
./alphanet.sh client get balance for money

You may be wondering what’s the difference between an account and a contract, since both have addresses and can take part in transactions. Let’s look at the technical white paper (note that this is a different paper than the language specification). Section 3.3 deals with smart contracts.

So they are essentially the same. The paper also has more information about the manager of a contract:

parameter string;
return string;
storage string;
code {DUP;
      DIP{CDR};
      CAR;
      SWAP;
      CONCAT;
      DUP;
      PAIR}

Save the contract to a file and originate it:

./alphanet.sh client originate contract appendString for my_identity transferring 100 from money running container:appendString.tz -init '"hello"'

You can use any file names or aliases you want, but make sure you replace my example names everywhere. I have an account with a pile of money named money, you can use the default my_account if your money is there. We can specify the initial storage with init.

If the origination seems to have worked, but you are getting a Not Found error when trying to check the balance, you can forget the contract with:

./alphanet.sh client forget contract appendString

Then you can try to originate it again and/or specify a fee to increase the chances of the contract being included in the next block.

We can initiate contract execution by sending a transaction to the contract address. If the contract is an account, the transaction just moves money which isn’t that interesting. If however, the contract contains some code, the money is added to the contract and the code is executed. The transaction can also use an arg flag to specify parameters that will then be used in the contract code.

Let’s see:

./alphanet.sh client transfer 0 from my_account to appendString -arg '"test1"'

The storage should become "hellotest1" and the same should be returned. You can check the storage with:

./alphanet.sh client get storage for appendString

Try to run the transaction a few times or change the input and see what happens!

How could we solve this issue?

One of the possibilities is to require any incoming transaction to contribute tez to the contract, and refuse those that don’t. How much the transaction should contribute depends on the length of the input, but Michelson doesn’t have an easy way to get it. The string type in Michelson is actually an array of up to 1024 bytes, so we could just ask everyone for 1024 tez.

code {PUSH tez "1024.00";
      AMOUNT;
      CMPLT;
      IF{FAIL}
        {DUP;
         DIP{CDR};
         CAR;
         SWAP;
         CONCAT;
         DUP;
         PAIR}}

CMPLT is syntax sugar for COMPARE and LT (less than), and lets us compare the amount of tez in the incoming transaction (AMOUNT) with the 1024 tez we are asking for using the contract. In case the transaction contains less than 1024 tez, we use the FAIL instruction to stop executing contract code and exit (this means the transaction won’t take place and any tez will stay where it was). Otherwise we execute the contract normally.

We can use this idiom to charge for using our contract in other contexts as well, whether we incur any storage fees or not. We can even refuse undesirable transactions in a similar manner when e.g. a caller fails to authenticate itself.

Although the storage fees will likely be adjusted before mainnet launch, using smart contracts to publish data this way is most likely going to be prohibitively expensive in practice. Luckily, most of the data that we might use in a contract is of fixed length (such as addresses or hashes). If a larger piece of data is required, it can be stored off the chain and a fixed length hash can be stored on the blockchain to prove authenticity.

For example, let’s write a simple contract that takes a string, calls our appendString contract, and stores the result. Try to write it yourself, or look at the end of this section to see the code. We can use storage to specify the target contract, and hold the result at the same time like so:

storage (pair string (contract string string));
              ^       ^
              |       |
         result       address of the target contract

When originating the contract, we will use the initial storage to specify the address of our appendString contract like so:

./alphanet.sh client originate contract coolName for my_identity transferring 100 from money running container:call.tz -init '(Pair "placeholder" "TZ1X4ukutiNUCNrEcBFAXghjpgubL8t5j4Zq")'
                                                                                                                                     ^             ^
                                                                                                                                     |             |
                                                                                                        result will be here eventually             address of appendString goes here

To call the other contract, we’ll use the TRANSFER_TOKENS instruction and we’ll need to have these things on the stack:

            [ string : tez : (contract string string) ]
              ^        ^      ^
              |        |      |
          input     amount    address of the target contract

Since the contract’s type is (contract string string) both the input parameter and the return value need to be a string.

Furthermore, we’ll need to have the current contract’s storage below those, so altogether it will look like this:

[ string : tez : (contract string string) : pair string (contract string string) ]

This is needed e.g. when a complex contract requires inputs from multiple contracts, the temporary result needs to be stored before handing off control to another contract.

After the contract has been executed, we will have a return value of type string on top of the stack.

[ string : pair string (contract string string) ]

We can call the contract as usual. It will execute its code – call the contract specified by its storage, and save the result of that call into the other part of the storage:

./alphanet client transfer 0 from money to coolName -arg '"knock knock"'

Check the result:

./alphanet client get storage for coolName

My code for this contract is here:

parameter string;
return string;
storage (pair string (contract string string));
code {DUP;
      CAR;
      DIP{CDR;
          DUP;
          CDR;
          PUSH tez "0.00"};
      TRANSFER_TOKENS;
      DIP{CDR};
      DUP;
      DIP{PAIR};
      PAIR};

Besides editor plugins, other projects with potential to improve the developer story of Michelson are compilers from other languages. There are a few of them already, like liquidity, which is a functional language with OCaml syntax that compiles to Michelson (and back). Other languages that compile to Michelson include fi and Juvix, a Haskell to Michelson compiler. These or similar newly created ones are likely to be used for writing more complicated contracts in the future.

This approach still has some unresolved issues with incentivizing truthful reporting, as is discussed e.g. in the Blockchain: The Oracle Problems talk by Paul Sztorc.

We’ve covered originating contracts on the blockchain, triggering their execution by sending transactions to them and some of the caveats of using storage. Since interaction with off-chain systems is required for most interesting use-cases of smart contracts, we will look at writing a simple oracle in the next article.

tzsuite: event driven library for Tezos

October 2, 2018
golang tezos michelson cryptocurrency blockchain smart contracts applications oracles automation

Getting started with Liquidity 2: authentication

July 31, 2018
easy liquidity cryptocurrency tezos michelson blockchain smart contracts

Getting started with Liquidity: coinflip

May 27, 2018
easy liquidity cryptocurrency tezos michelson blockchain smart contracts