Skip to main content

Compiling and deploying

Compiling a contract

In order to deploy a contract in a Tezos network, we first need to compile it. This is done using a compiler (aka LIGO compiler) that transforms LIGO code into Michelson code.

Michelson smart contracts are stored in a file with the .tz extension.

Here is how to transform a LIGO code into a Michelson code using the LIGO compiler in the command line.

ligo compile contract SOURCE_LIGO_FILE

Where:

  • SOURCE_LIGO_FILE is the path to your LIGO file containing the main function.

Example:

ligo compile contract examples/counter.ligo

The examples are detailed later in the chapter, see here.

You can store the Michelson output of the command above in .tz file in order to use it later when deploying the contract:

ligo compile contract SOURCE_LIGO_FILE > MICHELSON_FILE

Simulating (Dry-running) a contract

Testing a contract would be pretty tricky if you always had to set up a live node to run tests. Fortunately, an easy alternative is to use LIGO's built-in dry-run feature. Dry-running is a simulated execution of the smart contract as if it was deployed on a real chain. It works by simulating the main execution function, based on a mock storage value and a parameter.

ligo run dry-run [flags] SOURCE_LIGO_FILE 'ACTION(P)' 'STORAGE_STATE'

where:

  • ACTION(P) is a LIGO expression used to specify the action that triggers the associated entrypoint with the corresponding parameter p.
  • STORAGE_STATE is the state of the storage when simulating the execution of the entrypoint.

Example:

ligo run dry-run counter.ligo 'Increment(5)' 3
// Outputs: ( LIST_EMPTY() , 8 )

Defining the initial storage

The Michelson output of the following command can be used to init the storage when deploying the contract.

ligo compile storage SOURCE_LIGO_FILE 'STORAGE_STATE'

where:

  • STORAGE_STATE is a LIGO expression that defines the initial state of the storage.

Example:

ligo compile storage counter.ligo 5
// Outputs: 5

Invoking the contract with a parameter

The Michelson output of the following command can be used as the entrypoint name when invoking an entrypoint of the smart contract (as well as the parameters of that entrypoint).

ligo compile-parameter SOURCE_LIGO_FILE MAIN_FUNCTION 'ACTION(P)'

where:

  • ACTION(P) is a LIGO expression used to specify the action that triggers the associated entrypoint with the corresponding parameter p.

Example:

ligo compile parameter examples/counter.ligo main 'Increment(5)'
// Outputs: (Right 5)

Deploy and Invoke

Deploy

A smart contract must be deployed on the blockchain in order to be invoked. When deploying a smart contract on the blockchain, one must specify the initial state of the storage.

The deployment of a smart contract in Tezos is called "origination".

Here is the syntax for the Tezos command line to deploy a smart contract:

octez-client originate contract CONTRACT_NAME transferring AMOUNT_TEZ from FROM_USER \
running MICHELSON_FILE \
--init 'INITIAL_STORAGE' --burn-cap GAZ_FEE

where:

  • CONTRACT_NAME is the name given to the contract.
  • MICHELSON_FILE is the path for the Michelson smart contract code (.tz file).
  • AMOUNT_TEZ is the quantity of Tez being transferred to the newly deployed contract. If a contract balance reaches 0 then it is deactivated.
  • FROM_USER account from which the Tez are taken (and transferred to the new contract).
  • INITIAL_STORAGE is a Michelson expression. The --init parameter is used to specify the initial state of the storage.
  • GAZ_FEE is a specified maximal fee the user is willing to pay for this operation (using the --burn-cap parameter).

To get a Tezos client :

  • On GNU/Linux, the simplest way to get octez-client is through opam using opam install tezos. alternatives are available here
  • On MacOsX, the software is distributed through a brew formula with brew install tezos.

Invoke

Once the smart contract has been deployed on the blockchain (contract-origination operation baked into a block), it is possible to invoke an entrypoint from the smart contract using the command line. Here is the syntax of the Tezos command line to invoke a smart contract:

octez-client transfer AMOUNT_TEZ from USER to CONTRACT_NAME --arg 'ENTRYPOINT_INVOCATION' --dry-run

where:

  • AMOUNT_TEZ is the quantity of Tez being transferred to the contract.
  • CONTRACT_NAME is the name given to the contract.
  • ENTRYPOINT_INVOCATION is a Michelson expression of the entrypoint and the corresponding parameter. The --arg parameter specifies an entrypoint call.

⚠️ Notice that the --dry-run parameter simulates the invocation of the entrypoint.

Example

Let's consider a counter contract for our example.

Our counter contract will store a single int as its storage, and will accept an action variant in order to re-route our single main function to two entrypoints for add (addition) and sub (subtraction).

Create the file counter.jsligo :

type storage = int;

type parameter =
["Increment", int]
| ["Decrement", int]
| ["Reset"];

type ret = [list<operation>, storage];

// Two entrypoints

const add = (store : storage, delta : int) : storage => store + delta;
const sub = (store : storage, delta : int) : storage => store - delta;

/* Main access point that dispatches to the entrypoints according to
the smart contract parameter. */

const main = (action : parameter, store : storage) : ret => {
return [list([]) as list<operation>, // No operations
match (action, {
Increment:(n: int) => add (store, n),
Decrement:(n: int) => sub (store, n),
Reset :() => 0})]
};

Compile

ligo compile contract counter.jsligo > counter.tz

The command above outputs the following Michelson code:

Note that the output has been saved in the Michelson file counter.tz

{ parameter (or (or (int %decrement) (int %increment)) (unit %reset)) ;
storage int ;
code { UNPAIR ;
IF_LEFT { IF_LEFT { SWAP ; SUB } { ADD } } { DROP 2 ; PUSH int 0 } ;
NIL operation ;
PAIR } }

Initial storage

However, in order to originate a Michelson contract on Tezos, we also need to provide its initial storage value, we can use compile storage to compile the LIGO representation of the storage to Michelson.

ligo compile storage counter.jsligo 5
// Outputs: 5

Invocation parameter

The same rules apply for the parameters. We will need to use compile parameter to compile our action variant into Michelson, here's how:

ligo compile parameter counter.jsligo 'Increment(5)'
// Outputs: (Left (Right 5))

We can now use (Left (Right 5)), which is a Michelson value, to invoke our contract via octez-client.

Simulating

To dry-run the counter-contract, we provide a main function with a variant parameter of value Increment (5) and an initial storage value of 3.

ligo run dry-run counter.jsligo 'Increment(5)' 3
// Outputs: ( LIST_EMPTY() , 8 )

The simulation shows that our storage would have been incremented to 8.

Deploy

Now that we have verified that our code compiles well and that it was functional, we can deploy our contract on the blockchain.

This method consists in running a "mockup" Tezos chain on our computer, push the contract on the chain and send transaction to the chain to test the contract behavior.

First, create a temporary folder for the mockup chain by running

mkdir /tmp/mockup

Now start the node by running

octez-client \
--base-dir /tmp/mockup \
--mode mockup \
create mockup

This will run the node using the latest protocol and return a few address, aliased from bootstrap1 to 5. For other version, check octez-client list mockup protocols

octez-client \
--base-dir /tmp/mockup \
--mode mockup \
originate contract counterContract \
transferring 0 from bootstrap1 \
running counter.tz \
--init 10 --burn-cap 0.1

Note that you can simulate the deployment by adding the --dry-run parameter to the above command. boostrap1 and boostrap2 are users from the Tezos sandbox. Sandboxed mode is a way to run a 'localhost-only' instance of a Tezos network. Find out more about the sandboxed mode here.

Invoke

Let's invoke the entrypoint Increment(5) of the smart contract. Remember that the output of the compile parameter of this entrypoint was (Left (Right 5))

octez-client \
--base-dir /tmp/mockup \
--mode mockup \
transfer 5 from bootstrap1 to counterContract --arg '(Left (Right 5))'

Note that you can simulate the invocation by adding the --dry-run parameter to the above command.

Accessing storage

You can access the stored value with the following command:

octez-client \
--base-dir /tmp/mockup \
--mode mockup \
get contract storage for counterContract

Conclusion

In this section, we have seen how to use the LIGO compiler command lines to interact with a smart contract. We have seen how to compile a LIGO code in Michelson code, then simulate the behavior of its smart contract and, finally, the deployment and the invocation of entrypoints.