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.