Liquidity Tutorials — Hands-on Series — Part 2
By: Marty Stumpf
Tags: Development, Smart Contracts, Liquidity, Blockchain, Tezos

Photo by Weiwei Chen from Pexels

This is the second part of a tutorial series on writing smart contracts in Liquidity. See the first part here.

Last time we wrote the identity contract, and ran a simulation of it. This time, we’ll write a contract that sends one tez to another account. We’ll also deploy the contract to the testnet (Alphanet), and call it to see it in action!

The contract

The caller of this contract initiates a transfer from the contract’s account to the destination (receiver’s) address. The Liquidity code below is saved as send_one_tez.liq:¹

type storage = unit



let%entry main

(key : key_hash)

_storage =

let op = Account.transfer ~dest:key ~amount:1tz in

( [op], () )

In this contract:

the storage is unit.

is unit. the parameter is the address of the receiver of the 1 tez, of type key_hash.

is the address of the receiver of the 1 tez, of type key_hash. there is only one entry point, called main:

its inputs are:

the parameter of the contract, i.e., the address of the receiver of the transfer, of type key_hash. the storage, i.e., unit, of type unit. Because we don’t need to call this value in the entry point, it does not need to be named. The underscore before the variable name indicates this.

its output is a pair of operation list and storage:

the operation list has one element, op, of type operation, and it is the Account.transfer operation. More on this below. the storage, i.e., unit, of type unit. The storage is unaltered in this contract.

Let’s take a closer look at the Account.transfer operation. It has the following type signature:

dest:key_hash -> amount:tez -> operation

It takes two inputs, dest, of type key_hash, and amount, of type tez. In Liquidity, it is represented by tz. The output is of type operation. The contract executes an internal operation: a transfer, of the amount specified, from the contract to the implicit (default) account of dest. In this contract, we have specified the dest to be the parameter (named key) and the amount to be 1 tz.

Deploy and call

Now let’s verify that the contract does what it intends to do. We’ll first check the balances of the relevant parties. Then, we deploy and call the contract. After the call we check the balances again to see the change in balances.

Accounts and balances

In this example I have three accounts (adam, alice, and bob) activated. Follow this for instructions on activating accounts.

Adam deploys/originates the contract. Alice calls the contract. Bob is the recipient of the 1 tez.

To call the contract (with Liquidity), we need to know alice’s private key and hash, and bob’s hash.

Running tezos-client show address alice -S --show-secret shows the following for alice:

Hash: tz1ccqAEwfPgeoipnXtjAv1iucrpQv3DFmmS

Public Key: edpku5LTYXxXKTjmzj9qtHR6TTYH1K6JU7bFe8MyJs6qabUs9kiZes

Secret Key: unencrypted:edsk2gKnmssYQB5sNCAL6sCcgfPRZDuK1zixBowAQ1H1WCt78JxEhf

alice is a fake account on the testnet. Remember, NEVER disclose your secret key!

Running tezos-client show address bob shows the following for bob:

Hash: tz1Ra8yQVQN4Nd7LpPQ6UT6t3bsWWqHZ9wa6

Public Key: edpktqrmQgDqvdCZM4msb9eDpe2adQ6NDgE7Jax34ABPzvw2Pk9SaT

Before we call the contract, which will execute the transfer of 1 tez, let’s check the balances of the accounts:

tezos-client get balance for adam

11331.543993 ꜩ

tezos-client get balance for alice

29399.641915 ꜩ

tezos-client get balance for bob

7704.71877 ꜩ

Deploy

Compile send_one_tez.liq to .tz (by running liquidity send_one_tez.liq ). Deploy the contract with on the Tezos command line interface (CLI) with:

tezos-client originate contract sendtez for adam transferring 1 from adam running [path to]/send_one_tez.tz --burn-cap 0.43

I name the contract sendtez.

The contract is originated for Adam, i.e., Adam is the manager of the contract.

1 tez is transferred from Adam to the contract.

The --burn-cap option is needed because this operation will burn higher than the default of 0.

Alternatively, one can deploy/call a contract using Liquidity or the Liquidity online editor.

The output confirms that the contract is injected to the network with the address KT1Cm2wXCJVUb7QMqHEK4xFtvGh9XUMQ2Py8:

Node is bootstrapped, ready for injecting operations.

Estimated gas: 14470 units (will add 100 for safety)

Estimated storage: 430 bytes added (will add 20 for safety)

Operation successfully injected in the node.

Operation hash is 'ooAS5avXzYK8KsZc7e5DfY7o6Pd2trHvqkgsYFFRVbpn9eXSEkq'

Waiting for the operation to be included...

Operation found in block: BMVp7boyyk1r8mcEKyeG5kzDvUD6zB4zfXrbSDAHaSEFWCAsnLZ (pass: 3, offset: 0)

This sequence of operations was run:

Manager signed operations:

From: tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD

Fee to the baker: ꜩ0.001886

Expected counter: 40385

Gas limit: 14570

Storage limit: 450 bytes

Balance updates:

tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD ............. -ꜩ0.001886

fees(tz3WXYtyDUNL91qfiCJtVUX746QpNv5i5ve5,177) ... +ꜩ0.001886

Origination:

From: tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD

For: tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD

Credit: ꜩ1

Script:

{ parameter key_hash ;

storage unit ;

code { DUP ;

DIP { CDR @_storage_slash_1 } ;

DIP { DROP } ;

CAR @key_slash_2 ;

IMPLICIT_ACCOUNT ;

PUSH mutez 1000000 ;

UNIT ;

TRANSFER_TOKENS @op ;

UNIT ;

NIL operation ;

DUUUP ;

DIIIP { DROP } ;

CONS ;

PAIR } }

Initial storage: Unit

No delegate for this contract

This origination was successfully applied

Originated contracts:

KT1Cm2wXCJVUb7QMqHEK4xFtvGh9XUMQ2Py8

Storage size: 173 bytes

Paid storage size diff: 173 bytes

Consumed gas: 14470

Balance updates:

tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD ... -ꜩ0.173

tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD ... -ꜩ0.257

tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD ... -ꜩ1

KT1Cm2wXCJVUb7QMqHEK4xFtvGh9XUMQ2Py8 ... +ꜩ1



New contract KT1Cm2wXCJVUb7QMqHEK4xFtvGh9XUMQ2Py8 originated.

The operation has only been included 0 blocks ago.

We recommend to wait more.

Use command

tezos-client wait for ooAS5avXzYK8KsZc7e5DfY7o6Pd2trHvqkgsYFFRVbpn9eXSEkq to be included --confirmations 30 --branch BL8o7pZFbHAgAVheKMFLKWUwWJtSj6eaz7QvQLYojKJavmvTAyf

and/or an external block explorer.

Contract memorized as sendtez.

The output also shows that Adam paid for various fees to deploy the contract, and the contract’s balance is increased by 1 tez. We can check that the contract has a balance of 1 tez:

tezos-client get balance for sendtez

1 ꜩ

Call

Now we can call the contract. Let’s use Liquidity this time! Use the Liquidity command (see the full list of commands with liquidity --help ) --call [contract address] [entry point name] [receipent's key_hash] (in my case, bob’s key hash):

The output confirms that the call is successful:

Main contract Send_one_tez

Successful call to contract KT1Cm2wXCJVUb7QMqHEK4xFtvGh9XUMQ2Py8 in operation opANAeVfLmiVnXvgNVvtkuqMBFW73ZPPZQZ5SCkEpcjmZzSZo3A

To check that one tez was transferred from the contract to bob, let’s check their balances:

tezos-client get balance for sendtez

0 ꜩ

tezos-client get balance for bob

7705.71877 ꜩ

bob’s balance has indeed gone up by 1tz. alice paid for the transaction fees that calls the contract, so her balance is down by a little:

tezos-client get balance for alice

29399.591915 ꜩ

In the next tutorial, we’ll step up our game and write a contract that publishes authenticated data. Stay tuned!