Chapter 20 : Interactions

A contract can invoke another by emiting a transaction operation at the end of an entrypoint.


We have already seen the Tezos.transaction in chapter 17 in order to send money to an address. It is also possible to use Tezos.transaction to call an entrypoint from another contract. In that case, we store the transaction in a type operation which is a predefined type representing a contract invocation.

let <operation_name> : operation = Tezos.transaction (<parameter>, <mutez>, <contract>);

where :

  • parameter is the entry point to call on the target contract,
  • mutez is the amount to transfer,
  • contract is the contract we want to interact with.

To get the contract we want to call and its entry points, we can use :


The function take an address and return an optional contract (remember to use option). When no contract is found or the contract doesn’t match the type, None is returned.


The following example shows how a contract can invoke another by emiting a transaction operation at the end of an entry point. In our case, we have a counter.ligo contract that accepts an action of type parameter, and we have a proxy.ligo contract that accepts the same parameter type, and forwards the call to the deployed counter contract.

// counter.religo
type storage = int

type parameter =
| Increment (int)
| Decrement (int)
| Reset

type return = (list (operation), storage)

let main = ( (action,store) : (parameter, storage)) : return => {
    let new_storage = switch (action) {
    | Increment (n) => store + n
    | Decrement (n) => store - n
    | Reset         => 0
    (([] : list (operation)), new_storage);
// proxy.religo

type parameter =
| Increment (nat)
| Decrement (nat)
| Reset;

type storage = unit;

type return = (list (operation), storage);

let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address);

let proxy = ((action, store): (parameter, storage)) : return => {
  let counter : contract (parameter) =
    switch (Tezos.get_contract_opt (dest) : option (contract (parameter))) {
    | Some (contract) => contract;
    | None => (failwith ("Contract not found.") : contract (parameter));
  /* Reuse the parameter in the subsequent
     transaction or use another one, `mock_param`. */
  let mock_param : parameter = Increment (5n);
  let op : operation = Tezos.transaction (action, 0tez, counter);
  ([op], store)

Notice that :

  • the proxy function has the specific prototype of a Main function.
  • the proxy function returns a list of operation containing the newly created transaction.
  • the parameter type (counter parameter) has also been defined in proxy contract. The calling contract must know the parameter type of called contract.

Your mission

1- Consider each of our weapons is managed by a smart contract :

// weapon.religo
type storage = int

type parameter =
  Fire (int)
| Stop

type return = (list (operation), storage)

let main = ((action, store) : (parameter, storage)) : return => {
    let new_storage = switch (action) {
    | Fire (n) => n
    | Stop => 0
 (([] : list (operation)), new_storage);

⚠️ Notice that the weapon smart contract provides two entrypoints Fire and Stop.

2- Consider the contract in the editor which is responsible to control the left and right weapons. ⚠️ Notice the addresses of each weapon, and that the orders function fetch the corresponding contracts. (Variables right_laser and left_laser can be used as the target of a transaction). Your job is now to define the list of transactions sent to the weapon contracts. For this, start by creating operations a list of type operation.

⚠️ Notice that operations must be filled with transactions and is returned by the function orders.

3- Send a Fire(5) transaction to the right_laser contract, then Stop. Send a Fire(5) transaction to the left_laser contract, then Stop.