Liquidity Extensions

The first step is to declare the Liquidity extensions we need. Let's create a tests/techel.liq to write our extensions into:

external get_balance :
    [%stack: address] -> [%stack: tez]
    = "GET_BALANCE"
external get_storage :
    [%type: 'a] -> [%stack: address] -> [%stack: 'a option]
    = "GET_STORAGE"
external apply_operations :
    [%stack: operation list] -> unit
    = "APPLY_OPERATIONS"
external start_set_source :
    [%stack: address] -> unit
    = "SET_SOURCE { #"
external end_set_source :
    unit -> unit
    = "}"
external must_fail :
    [%stack: string option] -> [%stack: operation] -> [%stack: operation]
    = "MUST_FAIL string"

It is not crucial to understand these rules precisely, only the power they give us. And that power is the following functions. What they do precisely will become clear when we use them later on.

  • get_balance address: takes an address as parameter, and returns the balance of the contract at that address
  • get_storage [%type: 'g] address: takes a type and an address as parameter, and returns
    • Some of the storage of the contract at that address, if it has type 'g
    • None otherwise
  • apply_operations ops: takes a list of operations and applies them right away
  • start_set_source address and end_set_source (): a very, very dirty hack to define a scope where all operations created appear to have been created by whatever is at address
  • must_fail msg_opt op: tells techelson that the operation op must fail; this succeeds iff op fails and
    • msg_opt is None, or
    • msg_opt is Some msg and op failed precisely with string msg.

Thanks to Liquidity's file import mechanism, we can use these functions in any Liquidity file test.liq with Techel.get_balance, Techel.get_storage etc. by simply calling Liquidity as follows:

$ liquidity tests/techel.liq test.liq

NB: you only need to write tests/techel.liq once, and make sure you pass it to Liquidity when compiling your testcases, as in test.sh below.

So let's fill in the test.sh file so that it runs our tests. It takes the path to our (future) Liquidity test file(s) as argument, and compiles it/them along with all the contracts in contracts/ and the extensions we just defined. So, given some file tests/test.liq, the script

  • generates tests/test.liq.techel: contains the testcase and the contract(s) to test, and
  • runs techelson on tests/test.liq.techel.
#! /bin/bash

set -e

test_file="$1"

this_script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
techel_lib="$this_script_dir/tests/techel.liq"

# List of all the contracts.
contracts=""
for file in `find "$this_script_dir/contracts" -iname "*.liq"` ; do
    contracts="$contracts $file"
done

# File liquidity will compile to.
target="$test_file.techel"

echo "Compiling $test_file..."
echo
liquidity --no-annot --no-simplify --no-peephole $techel_lib $contracts -o $target $test_file
echo

# Running techelson on the target.
echo "Running test $target"
echo
techelson $target

Warning: function start_set_source uses a dirty hack so that it works as a Liquidity extension (similar to a SQL injection). This is a temporary solution, eventually this hack will not be necessary.