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