Chapter 20 : Interactions

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

Transactions

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 entrypoint 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 :

Tezos.get_contract_opt <address>

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.

Example

The following example shows how a contract can invoke another by emiting a transaction operation at the end of an entrypoint. 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.mligo

type parameter =
  Increment of nat
| Decrement of nat
| Reset

type return = operation list * int

 let main (p,s: action * int) : return =
  let storage =
    match p with
    | Increment n -> add (s, n)
    | Decrement n -> sub (s, n)
    | Reset -> 0
  in ([] : operation list), storage
// proxy.ligo

type parameter =
  Increment of nat
| Decrement of nat
| Reset

type storage = unit

type return = operation list * storage

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

let proxy (action, store : parameter * storage) : return =
  let counter : parameter contract =
    match (Tezos.get_contract_opt (dest) : parameter contract option) with
      Some contract -> contract
    | None -> (failwith "Contract not found." : parameter contract) in
  (* Reuse the parameter in the subsequent
     transaction or use another one, `mock_param`. *)
  let mock_param : parameter = Increment (5n) in
  let op : operation = Tezos.transaction action 0tez counter
  in [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.ligo
type storage = int

type parameter =
  Fire of int
| Stop

type return = operation list * storage

let main (action, store : parameter * storage) : return is
 (([] : operation list),
  match action with
    Fire n -> n
  | Stop -> 0
 )

⚠️ 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.