Liquidity Tutorials — Hands-on Series — Part 2
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.
- the parameter 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!
[1]: In the future, one may save the code in a .ligo file and deploy with LIGO via the CameLIGO syntax.