Tezos Hands on

Calicurum

Preparation
  • Docker images install
  • Git repository
Basic transaction
  • Try tezos-client
  • Account creation
  • Token transactions
Introduction to smart contract
  • Basic concepts of Tezos smart contracts
  • LIGO compiler
  • Contract compilation, deploy, and call

Preparation

Prep #1: Docker and images

  • Install Docker
  • Linux users: configure so that you can run docker command without sudo: (details)
  • Install 2 docker images
$ docker pull dailambda/tezos-handson:2019-08 ↩️
$ docker pull dailambda/ligo:2019-08 ↩️
  • Check images are working:
$ docker run dailambda/tezos-handson:2019-08 ↩️
hello tezos
$ docker run dailambda/ligo:2019-08 ↩️
hello tezos

Prep #2: Clone Git repository

$ git clone https://gitlab.com/dailambda/docker-tezos-hands-on -b tezos-hands-on-2019-08 ↩️
$ cd docker-tezos-hands-on ↩️

Contents

tezos-handson-node
A sandboxed node for hands-on
tezos-client
Command line wallet. You can do everything with it.
ligo
LIGO compiler
contracts/
Camligo contract samples

If you dare want to build Docker images…

Sources are available in
docker-node/ and docker-ligo/ of the repo.

Are You Ready?

Usual Tezos Development…

We usually start a Tezos node connecting to the test network called “Alpha net”:

But it takes time to sync your node to the network…

Today, We Use Sandbox

  • Closed in your computuer. No external net access required.
  • No need to sync with the test/main network.
  • No need to buy real Tezos tokens.
  • You can reset the environment easily.

Sandboxed Environment for Hands-on

Let’s start it:

$ ./tezos-handson-node ↩️

and keep it running.

Note: The node uses TCP port 18732.

Reset, if something goes wrong:

# Stop tezos-handson-node, then 
$ rm -rf .tezos-handson-node .tezos-handson-client ↩️
$ ./tezos-handson-node

Start-up Example

$ ./tezos-handson-node 
Starting /home/tezzy/tezos//tezos-node run --data-dir /home/tezzy/.tezos-handson-node --no-bootstrap-peers --private-mode --sandbox /home/tezzy/.tezos-handson-node/sandbox.json --connections=0
Waiting the node startup...
Aug 20 05:15:37 - node.main: Starting the Tezos node...
..
Aug 20 05:15:38 - node.main: The Tezos node is now running!
Node is up.
Activating Alpha protocol...
..
Injected BLChsc7x64kH

Check it is working

Open a new terminal, then:

$ ./tezos-client bootstrapped ↩️
Current head: BLNeoW9n94mD (timestamp: ..
Bootstrapped.

ICO Commitments

Tezos fundraiser donors receive commitment information to activate their Tezos accounts in Mainnet.

In this hands-on, 2 dummy commitment files are prepared:

$ ls commitments/
tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF.json
tz1X4maqF9tC1Yn4jULjHRAyzjAtc25Z68TX.json

Account Activation

$ ./tezos-client activate account alice with \
     commitments/tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF.json ↩️
..
Operation successfully injected in the node.
..
The operation has only been included 0 blocks ago.
..
Account alice (tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF) activated with ꜩ27182818.28459.
  • Careful of typo.
  • tz1xx..xx is the name (public key hash) of the account.
    alice is an alias, which you can choose what ever you want.
  • It may take several ten seconds.

What Happend?!

$ ./tezos-client activate account alice with commitments/tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF.json 
Node is bootstrapped, ready for injecting operations.
Operation successfully injected in the node.
Operation hash is 'oo6rb94mw9Z4rKexQhYDnSZeXXWmcZ65vcz77KTDTcGstYYeeuC'
Waiting for the operation to be included...
Operation found in block: BKqArov5fNKzptY81CLsacSTTPh7tPmCXoqeQ4pNzKCP6GtGiro (pass: 2, offset: 0)
This sequence of operations was run:
  Genesis account activation:
    Account: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... +ꜩ20000000

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for oo6rb94mw9Z4rKexQhYDnSZeXXWmcZ65vcz77KTDTcGstYYeeuC to be included --confirmations 30 --branch BL26XEjqN4uKAnEC52z6ijj3XibpbcM4ysPLUfcJqkMccyHriV3
and/or an external block explorer.
Account alice (tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF) activated with ꜩ20000000.

I do explain.

1. Check the connected node

The node must be well sync’ed with the network:

Node is bootstrapped, ready for injecting operations.

The sandbox is always bootstrapped, so no problem.

2. Inject the operation to the network

Operation ooyyy..yyy for the account activation was injected to the network:

Operation successfully injected in the node.
Operation hash is 'ooyyy..yyy'
Waiting for the operation to be included...

Waiting the operation is taken for a new block.

3. The Op. is now in a new block

There is a validator in the sandbox.

Detected the validator took the operation and made a new block Bzzz..zzz:

Operation found in block: Bzzz..zzz (pass: 2, offset: 0)
This sequence of operations was run:
  Genesis account activation:
    Account: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... +ꜩ20000000

tz1Mawer.. registered in the genesis block is now activated.

4. You may wait for more blocks.

The block may be still overridden by the consensus.

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for oo6rb94mw9Z4rKexQhYDnSZeXXWmcZ65vcz77KTDTcGstYYeeuC to be included --confirmations 30 --branch BL26XEjqN4uKAnEC52z6ijj3XibpbcM4ysPLUfcJqkMccyHriV3
and/or an external block explorer.
Account alice (tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF) activated with ꜩ20000000.

Under Nakamoto consensus, longer you wait, the certainity incrases exponentially.

In this hands-on, there is no point to wait.

Operation Processing in Tezos

The same as we saw for the activation:

  • Make an operation.
  • Upload the operation to the P2P network.
  • A validator makes a block with the operation.
  • The block is uploaded to the P2P network.
  • The certainity of the operation increases as the chain grows.

Account Query

How much do I have?

Ask the blockchain:

$ ./tezos-client get balance for alice ↩️
20000000 ꜩ

alice is an alias of tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF. Therefore,

$ ./tezos-client get balance for tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ↩️
20000000 ꜩ

Balance of the others

Tezos accounts are not anonymous.
You can check the balance of other’s. For example, tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV:

$ ./tezos-client get balance for \
      tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV ↩️
0.000001 ꜩ

Hard to type? The account name is found in file accounts.txt:

$ cat accounts.txt ↩️
tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV

Account Alias

You can give an alias to an account:

$ ./tezos-client add address jun \
        tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV ↩️

$ ./tezos-client get balance for jun ↩️
0.000001 ꜩ

Accounts known by tezos-client

$ ./tezos-client list known addresses ↩️
jun: tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLq..
alice: tz1MawerETND6bqJqx8GV3YHUrvMBCD.. (unencrypted sk known)
  • alice’s secret key (sk) is known. It is your account.
  • jun’s secret key is not available. It is not yours.

Keep your secret key secret!!
Once known, your account is owned by others.

Sending tokens

Send some to jun

$ ./tezos-client transfer 100 from alice to jun ↩️

Thanks! It prints lots:

$ ./tezos-client transfer 100 from alice to jun ↩️
Node is bootstrapped, ready for injecting operations.
Estimated gas: 10200 units (will add 100 for safety)
Estimated storage: no bytes added
Operation successfully injected in the node.
Operation hash is 'ooJZ1U114ibPfikZNrDSfmqg8Tcs9CzshL4w1vvsDa35DdB72bN'
Waiting for the operation to be included...
Operation found in block: BL6H8145DMyPeXBQBc2JPRKpVstJSYkhgyGSFFhEJQGAfYweQ8u (pass: 3, offset: 0)
This sequence of operations was run:
  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.001258
    Expected counter: 1
    Gas limit: 10000
    Storage limit: 0 bytes
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ........... -ꜩ0.001258
      fees(tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv,6) ... +ꜩ0.001258
    Revelation of manager public key:
      Contract: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      Key: edpkuSR6ywqsk17myFVRcw2eXhVib2MeLc9D1QkEQb98ctWUBwSJpF
      This revelation was successfully applied
      Consumed gas: 10000
  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.001186
    Expected counter: 2
    Gas limit: 10300
    Storage limit: 0 bytes
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ........... -ꜩ0.001186
      fees(tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv,6) ... +ꜩ0.001186
    Transaction:
      Amount: ꜩ100
      From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      To: tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV
      This transaction was successfully applied
      Consumed gas: 10200
      Balance updates:
        tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ100
        tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV ... +ꜩ100

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for ooJZ1U114ibPfikZNrDSfmqg8Tcs9CzshL4w1vvsDa35DdB72bN to be included --confirmations 30 --branch BLpBxE6wFBQcvNPyUg7sdodpZN3Xra2qGAAJXPG7Z7i9EM94ivj
and/or an external block explorer.

I do explain.

1. Check the connected node

Waiting for the node to be bootstrapped before injection...
Current head: BL.. (timestamp: .., validation: ..)
Node is bootstrapped, ready for injecting operations.

2. Gas estimation
    and operation injection

Estimated gas: 10200 units (will add 100 for safety)
Estimated storage: no bytes added
Operation successfully injected in the node.
Operation hash is 'oOOO..OOO'
Waiting for the operation to be included...

3. The operation is now in a new block

Operation found in block: BL6H8145DMyPeXBQBc2JPRKpVstJSYkhgyGSFFhEJQGAfYweQ8u (pass: 3, offset: 0)
This sequence of operations was run:
  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.001258
    Expected counter: 1
    Gas limit: 10000
    Storage limit: 0 bytes
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ........... -ꜩ0.001258
      fees(tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv,6) ... +ꜩ0.001258
    Revelation of manager public key:
      Contract: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      Key: edpkuSR6ywqsk17myFVRcw2eXhVib2MeLc9D1QkEQb98ctWUBwSJpF
      This revelation was successfully applied
      Consumed gas: 10000
  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.001186
    Expected counter: 2
    Gas limit: 10300
    Storage limit: 0 bytes
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ........... -ꜩ0.001186
      fees(tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv,6) ... +ꜩ0.001186
    Transaction:
      Amount: ꜩ100
      From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      To: tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV
      This transaction was successfully applied
      Consumed gas: 10200
      Balance updates:
        tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ100
        tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV ... +ꜩ100

Let’s see more details here.

3.1 Revealation the public key

The public key edpkuSR6ywqsk.. of alice must be revealed to the blockchain, so that others can verify alice’s operation with it:

  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.001258   👈 Fee to the validator
    Expected counter: 1
    Gas limit: 10000
    Storage limit: 0 bytes
    Balance updates:      👈 Fee is paid to the validator.
      tz1MawerETND6bqJqx8GV3YHUrvM ........... -ꜩ0.001258
      fees(tz1ddb9NMYHZi5UzPdzTZMY95zgv,6) ... +ꜩ0.001258
    Revelation of manager public key:
      Contract: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      Key: edpkuSR6ywqsk17myFVRcw2eXhVib2MeLc9D1QkEQb98..
      This revelation was successfully applied
      Consumed gas: 10000

3.2 The transaction

  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.001186              👈 Fee to the validator
    Expected counter: 2
    Gas limit: 10300
    Storage limit: 0 bytes
    Balance updates:                👈 Fee is paid to the validator.
      tz1MawerETND6bqJqx8GV3YHUrvM ........... -ꜩ0.001186
      fees(tz1ddb9NMYHZi5UzPdzTZMY..zgv,6) ... +ꜩ0.001186
    Transaction:
      Amount: ꜩ100                  👈 The amount of the transaction
      From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      To: tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV
      This transaction was successfully applied
      Consumed gas: 10200
      Balance updates:
        tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ100 👈 alice pays
        tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV ... +ꜩ100 👈 to jun

4. You may wait for more blocks.

The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for ooZZZ..ZZZ to be included --confirmations 30 --branch B..
and/or an external block explorer.

Check the balance

The token was really transferred? Check it:

$ ./tezos-client get balance for alice ↩️
19999899.997556 ꜩ
$ ./tezos-client get balance for jun ↩️
100.000001 ꜩ

The balance of alice should decrease by 100tz + the fee paid for the validator.

You cannot steal jun’s token

Since you do not know his secret key:

$ ./tezos-client transfer 100 from jun to alice ↩️
Error:
  Unknown secret key for tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV

alice’s secret key is known by tezos-client.
It is stored in .tezos-handson-client:

$ cat .tezos-handson-client/secret_keys ↩️
[ { "name": "alice",
    "value": "unencrypted:edsk3NDQq5Cmix9H15GkcoVhWo.." } ]

Gas, Storage, and Fee of Tezos

Who wants to issue opreations in Tezos must propose:

Cost Estimation

Gas limit
How much CPU power is required.
Storage limit
How much additional storage is required.

Incentive to the validator

Fee
Paid by the issuer to the validator, if the op. is taken into the new block.

What Validator Does

Compare the cost estimation and the fee:

Fee > Cost estimation
Take the operation into the new block to receive the fee.
Cost estimation > Fee
Operation will not be used for the new block.

Operation issuer should:

Propose proper fee
Operations with too low fee are never taken into the blockchain.
Should not cheat the validator with too low cost estimate
The operation fails and the fee is taken by the validator,
if actual operation cost exceeds the estimate.

Build your new account

Create a pair of secret and public keys. Easy:

$ ./tezos-client gen keys alice2
$ ./tezos-client list known addresses
alice2: tz1M9ZZccgEAgtDGMu8R7xniFt.. (unencrypted sk known)
jun: tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV
alice: tz1MawerETND6bqJqx8GV3YHUrv.. (unencrypted sk known)

Burn: fee to add storage

Sending from alice to alice2 10000ꜩ fails:

$ ./tezos-client transfer 10000 from alice to alice2
Fatal error:
  The operation will burn ꜩ0.257 which is higher than the configured burn cap (ꜩ0).
   Use `--burn-cap 0.257` to emit this operation.
Exiting

Additional storage is required to record the new account to the blockchain. 0.257ꜩ is required to burn.

$ ./tezos-client transfer 10000 from alice to alice2 \
      --burn-cap 0.257
...
      Balance updates:
        tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ10000
        tz1cMEsG9mvL8zyiGHppcCjcz1Et65zwidfg ... +ꜩ10000
        tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ0.257 👈 Burn

Smart Contract

How Tezos Smart Contracts Work

Accounts KTxxx..xxx can have smart contracts.

Contract state

  • Balance
  • Code
  • Persistent storage

Trigger

  • Code is executed at each transaction to the contract.

Tezos Contract is Pure Function

Input
  • Parameter given at the transaction.
  • Storage contents
  • Global constants
    (current account balance, amount of the transaction, the source of the transaction, etc)
Output
  • Operations (transactions, call of other smart contracts, etc.)
  • New storage contents
No side effects
Immutable. Purely functional.

Michelson

Stack VM for Tezos smart contracts, and its language.


# Return to the source

parameter unit;
storage unit;

code {
       CDR ;
       NIL operation ;
       AMOUNT;
       PUSH mutez 0;
       IFCMPEQ
         { }
         {
           SOURCE ;
           CONTRACT unit ;
           ASSERT_SOME ;
           AMOUNT ;
           UNIT ;
           TRANSFER_TOKENS ;
           CONS ;
         };
       PAIR;
     }

VM but high level:

  • Statically typed
  • \(\mathbb{N}\), \(\mathbb{Z}\) with arbitrary precision. No overflow.
  • Data types (options, lists, sets, map)
  • Cryptographic primitives

But bit hard 😝 to read and write…

CameLIGO

Today, we use LIGO(http://ligolang.org)
high level language for Tezos smart contracts.

Compiler
LIGO code is compiled down to Michelson
2 syntaxes
  • PascaLIGO (Pascal like) and CamLIGO (OCaml like)
Cheat sheet and samples

Still very new and glitchy, but already usable.

Disclaimer
to talented OCaml programmers

CamLIGO is not OCaml.

  • No recursions: let rec
  • Type inference is not fully automatic: ([] : int list)
  • Pattern match is not real pattern match.
    • No nested patterns: Some (Some x)
    • No constants in patterns. 1::[]

The First CamLIGO Contract

Replace the storage by the parameter string:

let main (parameter : string) (storage : string) =
  ( ([] : operation list),  parameter )

Puzzled?

Relax. I do explain.

Function Definition

let function_name (arg1 : type1) ... (argn : typen) =
  code
Arguments must be type-annotated:
(parameter : string)
Arguments are white space separated:
  • 😃 let f (x : ty1) (y : ty2) = ..
  • let f ( (x : ty1), (y : ty2) ) = ..
let main (parameter : string) (storage : string) =
  ( ([] : operation list),  parameter )

Contract Entrypoint

A function with 2 arguments of:

  • Parameter: here, parameter
  • Storage value: here, storage

returns a tuple (..., ...) of:

  • List of operations: here, empty: [] (with type annotation)
  • New storage value: here, parameter
let main (parameter : string) (storage : string) =
  ( ([] : operation list),  parameter )

Compilation

$ ./ligo compile-contract contracts/first.mligo main ↩️
  • Place the contract code at contracts/first.mligo
  • Specify the entrypoint function name main

If you made no mistake, the code should be compiled down to a Michelson code contracts/first.tz.

let main (parameter : string) (storage : string) =
  ( ([] : operation list),  parameter )

Look of Michelson Code

{ parameter string ;
  storage string ;
  code { {} ;
         { { { { DUP } ; CAR } ;
             { { { { DIP { DUP } ; SWAP } } ; CDR } ;
               { { { DIP { DUP } ; SWAP } ; NIL operation } ; PAIR } ;
               {} ;
               DIP { { DIP { DIP { DIP { {} } } } ; DROP } } } ;
             {} ;
             DIP { { DIP { DIP { {} } } ; DROP } } } ;
           DIP { { DIP { {} } ; DROP } } } } }

Puzzled?

No worry, I do not explain.

Contract Deployment

$ ./tezos-client originate contract \
        first for alice \
        transferring 0 from alice \
        running contracts/first.tz \
        --init '"initial value"' \
        --burn-cap 0.506 ↩️
  • first: Alias of the contract
  • for alice: Manager is alice
  • transferring 0 from alice: Initial balance is 0 paid by alice
  • running contracts/first.tz: The Michelson code
  • --init '"initial value"': The initial storage value
  • --burn-cap 0.506: For registration network fee

Did not work for you?

Few things you should check:

Check typo
  • Your account alias is alice?
  • Quotes are important: '"initial value"'
Got "Error: At line 1 character 0, blahblah
Limitation of Docker.
Please place first.tz under the directory with ligo.
Got “Fatal error” about the burn cap? Asked more?
Longer initial storage string costs you more.

If you are successful

$ ./tezos-client originate contract first for alice transferring 0 from alice running contracts/first.tz --init '"initial value"' --burn-cap 0.506 ↩️
Node is bootstrapped, ready for injecting operations.
Estimated gas: 16275 units (will add 100 for safety)
Estimated storage: 506 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'ooe54u2xReWTmbV6eWeKZf3SbWNRMGcFdyuVKDU5UpjYtM1EVDU'
Waiting for the operation to be included...
Operation found in block: BLCmgqwqMShuD38WYFabrD8o8XRf9Xp7W5ZyUkjQYYiK7ULF8qv (pass: 3, offset: 0)
This sequence of operations was run:
  Manager signed operations:
    From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
    Fee to the baker: ꜩ0.002139
    Expected counter: 3
    Gas limit: 16375
    Storage limit: 526 bytes
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ............ -ꜩ0.002139
      fees(tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv,10) ... +ꜩ0.002139
    Origination:
      From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      For: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      Credit: ꜩ0
      Script:
        { parameter string ;
          storage string ;
          code { {} ;
                 { { { DUP ; CAR } ;
                     { { { DUUP } ; CDR } ;
                       { { DUUP ; NIL operation } ; PAIR } ;
                       {} ;
                       DIP { { DIP { DIP { DIP { {} } } } ; DROP } } } ;
                     {} ;
                     DIP { { DIP { DIP { {} } } ; DROP } } } ;
                   DIP { { DIP { {} } ; DROP } } } } }
        Initial storage: "initial value"
        No delegate for this contract
        This origination was successfully applied
        Originated contracts:
          KT1NmLBEGeKrppHESUK9KEkHjj6i4677J3cS
        Storage size: 249 bytes
        Paid storage size diff: 249 bytes
        Consumed gas: 16275
        Balance updates:
          tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ0.249
          tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ0.257

New contract KT1NmLBEGeKrppHESUK9KEkHjj6i4677J3cS originated.
The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
  tezos-client wait for ooe54u2xReWTmbV6eWeKZf3SbWNRMGcFdyuVKDU5UpjYtM1EVDU to be included --confirmations 30 --branch BLTSxQK35VsHuFpvyFJRWkcvxZi2CEmigLvc3FWBarDPXZrFAEP
and/or an external block explorer.
Contract memorized as first.

I do explain.

Deploy Result

  • Gas limit: 16374, Storage limit 526 bytes
  • Proposing fee to the validator: ꜩ0.002139
  • The contract is owned by alice (tz1Mawer..)
  • The initial balance (credit) is ꜩ0
  • Michelson code and the initial value "initial value"
  • The originated contract address is KT1NmLBE..
  • Actual cost: Gas: 16275, Storage: 249 bytes
  • Network fee for the new storage: ꜩ0.249
  • Network fee for the contract origination: ꜩ0.257

The wallet knows the new contract

$ ./tezos-client list known contracts ↩️
first: KT1NmLBEGeKrppHESUK9KEkHjj6i4677J3cS
jun: tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV
alice: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF

Not addresses but contracts this time.

Check the state of the contract

$ ./tezos-client get script code for first ↩️
{ parameter string ;
  storage string ;
  code { .. } }

$ ./tezos-client get script storage for first ↩️
"initial value"

$ ./tezos-client get balance for first ↩️
0 ꜩ

Call the Smart Contract

Same as the simple transaction we did,
but specify the parameter with --arg

$ ./tezos-client transfer 0 from alice to first \
     --arg '"hello"' ↩️
..
    Transaction:
      Amount: ꜩ0
      From: tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF
      To: KT1NmLBEGeKrppHESUK9KEkHjj6i4677J3cS
      Parameter: "hello"
      This transaction was successfully applied
      Updated storage: "hello"
      Storage size: 241 bytes
      Consumed gas: 15762
..

Did not work for you?

Check typo
  • Your account alias is alice?
  • Commands are designed intentionally long to avoid mistakes.
Got “Fatal error” about the burn cap?
Extending the storage size costs you:
$ ./tezos-client transfer 0 from alice to first \
    --arg '"I am creative and give a very long string!"' ↩️
Fatal error:
  The operation will burn ꜩ0.029 which is higher than the configured burn cap (ꜩ0).
   Use `--burn-cap 0.029` to emit this operation.

Let’s check the result

$ ./tezos-client get script storage for first ↩️
"whatever you sent"

$ ./tezos-client get balance for first ↩️
100 ꜩ     👈 Non 0 if you transfer some tokens.

Congratulations!

You are now a Tezos engineer.

Smarter Contracts

Returning Operations

By returning non empty operation list, smart contracts can send tokens to other addresses, and call other smart contracts.

Smarter Contracts: Boomerang

If someone sends non-zero token, return it to the source. Otherwise, do nothing.

Function arguments:

  • Storage: nothing
  • Parameter: nothing

In ML, we use type unit for the type of no specific data. () is the value of the type: ( () : unit )

Smarter Contracts: Global Constants

Global constants to use:

  • amount: The amount of transaction
  • source: The address which initiated the transaction.

Smater Contracts: functions for ops.

Functions to create operations:

Operation.transaction
: 'a -> tez -> 'a contract -> operation
Transfer token to the specified contract with a parameter,
ex. Operation.transfer () 10tz contract
Operation.get_contract
: address -> _ contract Obtain the contract of the given address.
Type annotation is required to specify the contract type.
ex. (Operation.get_contract adrs : unit contract)

Smater Contracts: Boomelang

let main (param : unit) (storage: unit) =
  let ops = 
    if amount = 0tz then 
      ([] : operation list) 
    else 
      [ Operation.transaction () amount 
          (Operation.get_contract source : unit contract) ]
  in
  ( ops, () ) 
Conditional
if e1 then e2 else e3
Non empty list
[ 1 ; 2 ; 3 ]
Local definition
let v = e1 in e2    Bind the result of e1 to variable v.

Compile+Deploy

$ ./ligo compile-contract contracts/boomerang.mligo main

$ ./tezos-client originate contract boomerang for alice \
    transferring 0 from alice running contracts/boomerang.tz \
    --burn-cap 0.745

Then Call!

$ ./tezos-client transfer 10 from alice to boomerang
    Balance updates:
      tz1MawerETND6bqJqx8GV3YHUrvMBCD.. ............ -ꜩ0.004347
      fees(tz1ddb9NMYHZi5UzPdzTZMYQQZoM95zgv,15) ... +ꜩ0.004347
    Transaction:
      ..
      Balance updates:
        tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... -ꜩ10
        KT1Lw7gDyYr2MFUVJLyA2sQuCSjsc1krcAkb ... +ꜩ10
    Internal operations:
      Transaction:
        ..
        Balance updates:
          KT1Lw7gDyYr2MFUVJLyA2sQuCSjsc1krcAkb ... -ꜩ10
          tz1MawerETND6bqJqx8GV3YHUrvMBCDasRBF ... +ꜩ10

Check the token was returned to alice.

Advanced Excersize

Simple banking: bank

Parameter type
unit
Storage type
Table of deposits, (address, tez) map: you can lookup the balance of each address.
Behaviour
  • If transaction amount is not 0tz, update the sender’s balance in the map.
  • If amount is 0tz, send the balance of the sender recorded in the map. Then, reset the balance of sender of the map.

Hints

./contracts/cheat-sheet.mdを参考に。

Global constants
amount, sender
The transferred amount is 0 or not:
amount = 0tzamount <> 0tz
Map operations:
Map.find_opt, Map.update, Map.remove
Branching by the result of Map.find_opt:
Use pattern match:
match result with None -> .. | Some tez -> ..

Hints

Deployment
You can give the initial empty map value by --init '{}'
Call
You will need --burn-cap when the call makes the size of map

Yet Another Advanced Excersize

The DAO attack used a contract attacker with the following behavior:

  • The initial balance is 100tz
  • If received 0tz, transact 10tz to bank
  • If received non 0tz,
    • If its balance exceeds 200tz, do nothing.
    • Otherwise, transact 0tz to bank

If transactions are implemented as function calls (in Ethereum), attacker can steal tokens from bank more than attacker’s deposit. Is the same possible in Tezos?