Usage

There are two tools provided by Liquidity.

  1. liquidity, the compiler. It can be used to:
    • compile Liquidity files (.liq) to Michelson
    • decompile Michelson files (.tz) to Liquidity
  2. liquidity-client, a client to interact with Liquidity contracts on Dune Network.

We recommend to read the Manual Pages if you want to see all options and examples for each command. The rest of this section shows some typical command usage if you want to get started immediately.

Compiling a Liquidity file

Let’s take a very simple Liquidity contract, stored in a file simple.liq:

type storage = int

let%entry main (parameter : int) storage =
  ( [], storage + parameter )

This contract will add its argument to the storage, and do nothing else.

To compile the file, we can use:

─➤ liquidity simple.liq
Main contract Simple
File "simple.tz" generated

The liquidity compiler will try to compile any file with a .liq extension provided on the command line.

Let’s have a look at the generated simple.tz file:

parameter int;
storage int;
code { DUP ;
       DIP { CDR } ;
       CAR ;
       SWAP ;
       ADD ;
       NIL operation ;
       PAIR };

Note that we can use a more compact version, on a single line:

─➤ liquidity-mini --compact simple.liq
Main contract Simple
File "simple.tz" generated

 ─➤ cat simple.tz
parameter int; storage int; code { DUP ; DIP { CDR } ; CAR ; DUP ; SWAP ; DROP ; SWAP ; ADD ; NIL operation ; PAIR };

In case of an error, for example if we set the storage to type nat instead of int, the compiler will provide the location of the error in a standard format (most editors can parse it so you can jump with a single keystroke to the error location):

─➤ liquidity simple.liq
simple.liq:4.2-4.48: Error: Type error:  Unexpected type for return value.
Expected type:
  (operation list * nat)
Actual type:
  (operation list * int)
(exit 1)

-> More details on the compiler.

Decompiling a Michelson file

Let’s decompile the simple.tz file from the previous section:

─➤ liquidity simple.tz
Program "simple.tz" parsed
File "simple.tz.liq" generated

We can now check the result of the decompilation:

─➤ cat simple.tz.liq
[%%version 0.4]
type storage = int
[%%entry
  let main (parameter : int) (storage : storage) =
    (([] : operation list), (storage + parameter)) ]

The liquidity tool will decompile in the same way any file with the .tz extension provided on the command line.

Interacting with a Dune node

Liquidity comes with a Dune Network client (also usable on the Tezos network) which allows interactions with a node using Liquidity smart contracts and Liquidity syntax.

Running a simulation of the contract

If you have access to a node (for instance locally on port 8733), you can run contract call simulations of a contract by specifying its current storage value, the entry point and the transaction parameter:

> liquidity-client run \
    --node http://127.0.0.1:8733 \
    --amount 2DUN \
    --files tests/others/demo.liq \
    default '"candidate 1"' 'Map ["candidate 1", 0; "candidate 2", 0]'

Main contract Demo
tests/others/demo.liq:13.4-13.61: Failed at runtime: in /chains/main/blocks/head/helpers/scripts/run_code
Failed with "Not enough money, at least 5DUN to vote"
> liquidity-client run \
    --node http://127.0.0.1:8733 \
    --amount 5DUN \
    --files tests/others/demo.liq \
    default '"candidate 1"' 'Map ["candidate 1", 0; "candidate 2", 0]'

Main contract Demo
Map [("candidate 2", 0); ("candidate 1", 1)]
# Internal operations: 0

-> More details on run.

Deploying a contract

To deploy a contract you need to forge a deployment operation, sign this operation and inject it to a node. This can be performed separately or all at once with the command deploy.

Deploying a contract directly (unsafe)

Deploying a delegatable but non-spendable smart contract whose Liquidity source code is contained in file contract.liq, whose initializer takes one string parameter as argument, and with initial balance 2tz:

liquidity-client deploy \
  --node http://127.0.0.1:8733 \
  --amount 2DUN \
  --private-key edsk2gL9deG8idefWJJWNNtKXeszWR4FrEdNFM5622t1PkzH66oH3r \
  --files contract.liq \
  '"first"'

Because we give the private key as an argument (notice that this process is unsafe, and should only be used with private keys not associated with real accounts on the mainnet) to sign the transaction, we don’t need to specify the source which will be inferred as being the corresponding public key hash.

-> More details on deploy.

Deploying a contract with an offline signature

The preferred way to proceed is to do this operation in three separate phase, the second one being the offline signature.

First we need to produce (forge) an unsigned serialized deployment operation:

> liquidity-client forge-deploy \
    --files tests/others/multisig.liq \
    --node http://127.0.0.1:8733 \
    --source dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F \
    'Set [dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F]' 1p > op.bytes

Using the default client we can then sign this operation with an account my_account on an offline machine. If this accounts corresponds to a hardware wallet (like a ledger nano S) in the Dune client, you will be required to confirm the signature. If this accounts in an encrypted private key you will be asked to input your password:

> dune-client sign operation op.bytes for my_account --out op.signed

Node: http://localhost:18735
Parsed operation:
  Manager signed operations:
    From: dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F
    Fee to the baker: đ0.009661
    Expected counter: 1
    Gas limit: 70221
    Storage limit: 2874 bytes
    Balance updates:
      dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F ........... -đ0.009661
      fees(dn1G8vdxwTcD7Nqf5ewF8FAj6bRG8iqtTgba,0) ... +đ0.009661
    Origination:
      From: dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F
      Credit: đ0
      Script:
        ...
      Initial storage:
        (Pair { "dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F" } (Pair {} (Pair 1 1)))
      No delegate for this contract
      This origination was successfully applied
      Originated contracts:
        KT1JS5QEB64SvLGRPBYxpmDG8obHoeGJZG5D
      Storage size: 2617 bytes
      Paid storage size diff: 2617 bytes
      Consumed gas: 70221
      Balance updates:
        dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F ... -đ2.617
        dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F ... -đ0.257

Would you like to sign this operation? [y/N]: y
Yes
Signature: edsigtxAkTRs3xqdKmTfSitdHc4o7Msjsg5bfSPCoMUS6mE3t89sAQWz6V8MJsfY3G4pTShBSEV5pXM66QZSCKpewxU5WCK345X
Serialized signed operation written to op.signed

You can now inject the signed operation on the newtork by simply issuing:

> dune-client inject operation op.signed

Parsed operation:
  ...
Would you like to inject this operation? [y/N]: y
Yes
Operation successfully injected in the node.
Operation hash is 'op47tCnmZkn6LToM4Yp8pTydesjJ3GWXhDrE2hFryvb9SAufRwm'
Waiting for the operation to be included...
This sequence of operations was run:
  ...
New contract KT1Q69vbqSQmWDGZMoMde2BT7nBv5uj3sFG7 originated.

-> More details on forge deploy.

Calling a contract

To call an already deployed smart contract you need to forge a transfer operation, sign this operation ans inject it to a Dune node. This can be performed separately or all at once with the command call.

Calling a contract directly (unsafe)

The following command will call the demo.liq contract deployed at address KT1Ukta5wAt5R87U2awCoYHJAVA38FeptagD on the testnet:

> liquidity-client call \
    --node http://testnet-node.dunscan.io \
    --amount 5DUN \
    --private-key edsk2gL9deG8idefWJJWNNtKXeszWR4FrEdNFM5622t1PkzH66oH3r \
    --files tests/others/demo.liq \
    KT1Ukta5wAt5R87U2awCoYHJAVA38FeptagD main '"ocaml"'

Main contract Demo
Successful call to contract KT1Ukta5wAt5R87U2awCoYHJAVA38FeptagD in operation oosA6qjVjtFbE9tGsrzHtjB6zk27R3yRH61wKD55WJ1WWiVjhy2

-> More details on call.

Calling a contract with an offline signature

The preferred way to proceed is to do this operation in three separate phase, the second one being the offline signature.

First we need to produce (forge) an unsigned serialized deployment operation:

> liquidity-client forge-call \
    --node http://testnet-node.dunscan.io \
    --amount 5DUN \
    --source dn1GLMm5dMXRxCwqmkV22keRCcoWwrrani9F \
    --files tests/others/demo.liq \
    KT1Ukta5wAt5R87U2awCoYHJAVA38FeptagD main '"ocaml"' > op.bytes
> dune-client sign operation op.bytes for my_account --out op.signed

...

Save this signature. You can now inject the signed operation on the network with the same command as above.

-> More details on forge call.

Generating initial storage

> liquidity-client init-storage \
    --node http://testnet-node.dunscan.io \
    --files tests/others/demo.liq \
    '"this"'

Main contract Demo
Constant initial storage generated in "tests/others/demo.liq.init.tz"
> cat tests/others/demo.liq.init.tz

{ Elt  "ocaml" 0 ; Elt  "pro" 0 ; Elt  "this" 0}

-> More details on init-storage.