More examples
Function
The example below comes from Tezos Academy and represents a modification of the id of a spaceship
.
So the following function takes a string my_ship
as input, modifies the third character to 1 and assigns the result to a constant modified_ship
.
//function_example.jsligo
type ship_code = string;
let my_ship: ship_code = "020433";
// Modify the code below
const modify_ship = (my_ship: ship_code): ship_code => {
const modified_ship: ship_code = String.sub(0 as nat, 2 as nat, my_ship) + "1" + String.sub(3 as nat, 3 as nat, my_ship);
return modified_ship;
}
You can call the function modify_ship
defined above by using the LIGO dev tools:
ligo run evaluate-call function_example.jsligo --entry-point modify_ship '("020433")'
# Outputs: '021433'
Record
The structured type record
is here, used to define the coordinates of a planet in the solar system.
type coordinates = {
x: int,
y: int,
z: int
};
let earth_coordinates: coordinates = {
x: 2,
y: 7,
z: 1
};
let modified_earth_coordinates = {...earth_coordinates, z: 5};
A functional update
{...earth_coordinates, ...
instruction takes arecord
data structure to be updated and a subset of the fields to update, then modifies the recorded data structure in accordance to the new specified subset of fields.
Main function
In LIGO, the design aims to have one main function called main
, which dispatches the control flow according to its parameter. The functions used for those actions are called entrypoints.
In the example below:
- The functions
set_ship_code
andgo_to
are the entrypoints. Set_ship_code
andGo_to
are the associated actions.
type parameter =
| ["Set_ship_code", string]
| ["Go_to", string];
type storage = {
ship_code: string,
destination: string
};
type return_ = [list<operation>, storage];
const set_ship_code = (input_string: string, store: storage): return_ =>
[(list([]) as list<operation>), {...store, ship_code: input_string}];
const go_to = (input_string: string, store: storage): return_ =>
[(list([]) as list<operation>), {...store, destination: input_string}];
const main = (action: parameter, store: storage): return_ =>
match(action, {
Set_ship_code: (input_string: string) => set_ship_code(input_string, store),
Go_to: (input_string: string) => go_to(input_string, store)
});
Option
The example below comes from Tezos Academy and deals with the modification of the weapon power of a spaceship to illustrate the use of option types.
In the code below, we can see that the weapons variable are defined as a mapping between the name of each weapon and its corresponding input of power. Here, we want to increase the power of the Main Laser but the mapping returns an optional result as it might not be found in the mapping. We define the constant main_laser_power
as an optional int
by selecting Main Laser from the weapons mapping.
We write a pattern matching the main_laser_power
:
- If it exists, it increases the power of the Main Laser by 1 (use
i
as a temporary matching variable). - If it does not exist in the mapping, it will fail with
Weapon not found
.
type weapon_power = map<string, int>;
const main = (_p: unit, _store: weapon_power) : [list <operation>, weapon_power] => {
let weapons: weapon_power =
Map.literal(list([
["Main Laser", 5],
["Right Laser", 2],
["Left Laser", 3]
]));
// Type your solution below
let main_laser_power: option<int> = Map.find_opt("Main Laser", weapons);
weapons = match(main_laser_power, {
Some: i => Map.update("Main Laser", Some(i + 1), weapons),
None: () => failwith("Weapon not found")
});
return [
(list([]) as list <operation>),
weapons
];
};
Full example - FundAdvisor
This example illustrates the communication between contracts (with get_entrypoint_opt LIGO function) and lambda patterns allowing you to modify a contract that is already deployed. The example illustrated implementing, deploying and interacting with Tezos smart contracts.
The Fund and its advisor
The indice
contract represents a fund value, and the advisor
contract gives advice on investing in this fund.
Transaction workflow
The advisor
contract can be invoked to request the fund value to the indice
contract (via a transaction). The indice
contract receives the request (transaction)cand will send back the requested value. When the advisor
contract receives the fund value, it can apply the algorithm to check if it is worth investing!
FIGURE 1: FundAdvisor
The resulting advice is stored in the storage (in the result field).
Lambda pattern
The real business logic of the advisor
smart contract lies in the lambda function, which is defined in the storage.
The storage can be modified so does the business logic (lambda).
Therefore an entrypoint with ChangeAlgorithm
is provided to modify the algorithm that computes the worth of the investment.
Lambda pattern Changing the behavior of a smart contract can be done by customizing its implementation through the lambda functions. The idea is to implement the smart contract logic in a lambda function that can be modified after the contract is deployed. Find out more about lambda here.
Requirement
- A LIGO compiler, and a code editor installed. If not, go there.
- A sandboxed mode ready.
Sandboxed mode
This part will explain to you how to run a 'localhost-only' instance of a Tezos network.
Run a sandboxed node
For instance, if you want to run a local network with two nodes, in the first terminal, the following command will initialize a node listening for peers on port 19731 and listening for RPC on port 18731.
./src/bin_node/octez-sandboxed-node.sh 1 --connections 1
To launch the second node, run the following command in another terminal, and it will listen to port 19739 and 18739:
./src/bin_node/octez-sandboxed-node.sh 9 --connections 1
Use the sandboxed client
Once your node is running, open a new terminal and initialize the sandboxed
client data in a temporary directory:
eval `./src/bin_client/octez-init-sandboxed-client.sh 1`
Activate a protocol
octez-activate-alpha
Find out more about sandboxed mode here
First contract - Indice
The indice contract represents a fund value.
Defining storage and entrypoints
Let's create a file indice_types.jsligo
to put all the needed type definitions.
indiceStorage
is an integer type that can have the value of an equity.indiceEntrypoints
determines how the contract will be invoked:- By increasing the value of the contract storage by using
["Increment",int]
. - By decreasing it with
["Decrement",int]
. - By sending its value to another contract that has called it, using
["SendValue",unit]
.
- By increasing the value of the contract storage by using
indiceFullReturn
determines the return type of the main function.
// indice_types.jsligo
type indiceStorage = int;
type indiceEntrypoints =
| ["Increment", int]
| ["Decrement", int]
| ["SendValue"];
type return_ = [list <operation>, indiceStorage];
Note that the
sendValue
entrypoint does not take any parameter.
Defining the main function
Now let's move to the file indice.jsligo
that includes the previous file indice_types.jsligo
and create the main function indiceMain
with writing #include "indice_types.jsligo"
at the beginning of the script.
// indice.jsligo
#include "indice_types.jsligo"
const indiceMain = ([action, store] : [indiceEntrypoints, indiceStorage]) : return_ => {
return [(list([]) as list <operation>),store];
};
Let's keep the block empty for now and see if it compiles correctly.
ligo compile contract --entry-point indiceMain indice.jsligo
This should return:
{ parameter (or (or (int %decrement) (int %increment)) (unit %sendValue)) ;
storage int ;
code { UNPAIR ;
IF_LEFT { IF_LEFT { SWAP ; SUB } { ADD } } { DROP 2 ; PUSH int 0 } ;
NIL operation ;
PAIR } }
Now let's implement the three entrypoints:
// indice.jsligo
#include "indice_types.jsligo"
const indiceMain = ([action, store] : [indiceEntrypoints, indiceStorage]) : return_ => {
return match (action, {
Increment: (n: int) => increment ([store, n]),
Decrement: (n: int) => decrement ([store, n]),
SendValue: () => sendValue(store)});
};
Defining the entrypoints
Increment
The increment
function takes two parameters:
s
the initial value of the storage.param
of typeint
, which is the value that will be added to the storage,
This function's return type is return_
and returns:
- an empty list of operations,
- a modified storage with a new value of
s + param
.
//indice.jsligo
const increment = ([s, param] : [indiceStorage, int]) : return_ => [list([]) as list <operation>,s + param];
Decrement
The decrement
function takes two parameters:
s
the initial value of the storage.param
of typeint
, which is the value that will be removed from the storage,
This function's return type is indiceStorage
and returns:
- a modified storage with a new value of
s - param
.
//indice.jsligo
const decrement = ([s, param] : [indiceStorage, int]) : return_ => [list([]) as list <operation>,s - param];
SendValue
The sendValue
function takes two parameters:
param
of typeunit
, which means it takes no parameter,s
the initial value of the storage.
This function's return type is return_
and it returns:
- a list of operations containing a transaction,
- the initial storage that is not modified.
The predefined function Tezos.get_entrypoint_opt
can be used to retrieve the definition of a single entry point. %receiveValue
is the label of the entrypoint that will be defined in the advisor contract.
When the function get_entrypoint_opt
does not find any contract at a given address
, or if the contract doesn't match the type, then None
is returned.
Note that the
Tezos.get_entrypoint_opt
function is a two-way communication solution between contract that are already deployed.
//indice.jsligo
const sendValue = (s : indiceStorage) : return_ => {
const c_opt : option<contract<int>> = Tezos.get_entrypoint_opt("%receiveValue", Tezos.get_sender());
const receiver : contract<int> =
match(c_opt,{
Some :(c) => c,
None : () => failwith("sender cannot receive indice value")
});
const op : operation = Tezos.transaction(s, 0 as mutez, receiver);
const txs : list<operation> = list([op]);
return [txs,s];
}
Let's compile again the main function to be sure we made no mistakes.
ligo compile contract --entry-point indiceMain indice.jsligo
Second contract - Advisor
Remember that the advisor
contract can be invoked to request the value of the fund from the indice
contract (via a transaction). The indice
contract receives the request transaction and will send back the requested value. When the advisor
contract receives the fund value, it can apply the algorithm to check that it is worth investing.
Defining storage and entrypoints
In a new file called advisor_types.jsligo
we define:
advisorStorage
which is a record type containing three fields:indiceAddress
of typeaddress
to communicate with theindice
contract.algorithm
that takes anint
as a parameter and returns abool
, depending on the business logic.result
that istrue
if the investor should invest andfalse
otherwise.
advisorEntrypoints
that determines how the contract will be invoked:- By receiving an integer value from another contract's storage with
["ReceiveValue",int]
. - By requesting this value with
["SendValue"]
. - By modifying the algorithm that computes the worth of the investment with
["ChangeAlgorithm",advisorAlgo]
.
- By receiving an integer value from another contract's storage with
advisorFullReturn
that determines the return type of the main function.
//advisor_types.jsligo
type advisorAlgo = ((a : int) => bool);
type advisorStorage = {
indiceAddress : address,
algorithm : advisorAlgo,
result : bool,
};
type advisorEntrypoints =
| ["ReceiveValue",int]
| ["RequestValue"]
| ["ChangeAlgorithm", advisorAlgo]
;
type advisorFullReturn = [list<operation>, advisorStorage];
Defining the main function
Let's create another file advisor.jsligo
that will include the previous file advisor_types.jsligo
and create the main function.
//advisor.jsligo
#include "advisor_types.jsligo"
const main = (ep : advisorEntrypoints, store : advisorStorage) : advisorFullReturn =>
match(ep,{
ReceiveValue : (p) => execute(p, store),
RequestValue : () => request(store),
ChangeAlgorithm: (p) => change(p, store)
});
Defining the entrypoints
ReceiveValue
Symmetrically to the SendValue
function defined for the indice contract, we define the RequestValue
function, so that the two-way communication can be complete.
The request
function takes one parameters:
s
the initial value of the storage.
This function's return type is advisorFullReturn
and returns:
- a list of operations containing a transaction,
- the initial storage that is not modified.
const request = (s : advisorStorage) : advisorFullReturn => {
const c_opt : option<contract<unit>> = Tezos.get_entrypoint_opt("%sendValue", s.indiceAddress);
const receiver : contract<unit> =
match(c_opt,{
Some :(c) => c,
None : () => failwith("indice cannot send its value")
});
const op : operation = Tezos.transaction(unit, 0 as mutez, receiver);
const txs : list<operation> = list([op]);
return [txs, s];
};
RequestValue
The execute
function takes two parameters:
indiceVal
of typeint
, which is the value that will be passed in the algorithm,s
the initial value of the storage.
This function's return type is advisorFullReturn
and returns:
- an empty list of operations,
- a modified storage with a new value for
s.result
that will be the boolean return of the algorithm.
const execute = (indiceVal : int, s : advisorStorage) : advisorFullReturn => {
return [list([]), {...s, result : s.algorithm(indiceVal)}];
};
ChangeAlgorithm
change
function takes two parameters:
p
of typeadvisorAlgo
which is the algorithm function corresponding to the wanted business logic.s
the initial value of the storage.
This function's return type is advisorFullReturn
and returns:
- an empty list of operations
- a modified storage with a new value for
s.algorithm
.
const change = (p : advisorAlgo, s : advisorStorage) : advisorFullReturn =>
[ list([]), {...s ,algorithm : p}];
Let's compile the main function.
ligo compile contract advisor.jsligo
Dry-run, compilation and deployment
Indice contract
//indice.jsligo
#include "indice_types.jsligo"
const increment = (param : int, s : indiceStorage) : indiceFullReturn =>
[list([]), s + param];
const decrement = (param : int, s : indiceStorage) : indiceFullReturn =>
[list([]), s - param];
const sendValue = (s : indiceStorage) : indiceFullReturn => {
const c_opt : option<contract<int>> = Tezos.get_entrypoint_opt("%receiveValue", Tezos.get_sender());
const receiver : contract<int> =
match(c_opt,{
Some : (c) => c,
None : () => failwith("sender cannot receive indice value")
});
const op : operation = Tezos.transaction(s, 0 as mutez, receiver);
const txs : list<operation> = list([op]);
return [txs, s];
};
const indiceMain = (ep : indiceEntrypoints, store : indiceStorage) : indiceFullReturn =>
match(ep,{
Increment : (p) => increment(p, store),
Decrement : (p) => decrement(p, store),
SendValue : () => sendValue(store)
});
Simulation
Let's simulate the indice contract with the increment action using 5
as a parameter and initial storage of 0
.
ligo run dry-run --entry-point indiceMain indice.jsligo 'Increment(5)' '0'
This should return:
( LIST_EMPTY() , 5 )
As expected, there is an empty list of operations, and the storage has been incremented by 5.
To be sure let's modify the initial value of the storage to 4
.
ligo run dry-run --entry-point indiceMain indice.jsligo 'Increment(5)' '4'
This should return:
( LIST_EMPTY() , 9 )
Let's simulate another entrypoint, SendValue
:
ligo run dry-run --entry-point indiceMain indice.jsligo 'SendValue()' '3'
This command should return:
failed with: "sender cannot receive indice value"
This is because the contract is not deployed yet. This is the limit of this simulation.
Compilation
Now, let's prepare the parameters and the storage.
ligo compile storage --entry-point indiceMain indice.jsligo '0'
#output: 0
ligo compile parameter --entry-point indiceMain indice.jsligo 'Increment(5)'
#output: (Left (Right 5))
ligo compile parameter --entry-point indiceMain indice.jsligo 'Decrement(5)'
#output: (Left (Left 5))
ligo compile parameter --entry-point indiceMain indice.jsligo 'SendValue()'
#output: (Right Unit)
Reminder:
0
,(Left (Right 5))
,(Left (Left 5))
,(Right Unit)
are Michelson expressions that can also be used as parameters inoctez-client
command lines.
We can now compile the main function and save the output in an indice.tz
file.
ligo compile contract --entry-point indiceMain indice.jsligo > indice.tz
This should return:
{ parameter (or (or (int %decrement) (int %increment)) (unit %sendValue)) ;
storage int ;
code { UNPAIR ;
IF_LEFT
{ IF_LEFT { SWAP ; SUB } { ADD } ; NIL operation }
{ DROP ;
SENDER ;
CONTRACT %receiveValue int ;
IF_NONE { PUSH string "sender cannot receive indice value" ; FAILWITH } {} ;
PUSH mutez 0 ;
DUP 3 ;
TRANSFER_TOKENS ;
SWAP ;
NIL operation ;
DIG 2 ;
CONS } ;
PAIR } }
Deployment
Note : look on previous chapter about deployment to run a local sandbox
To display the list of deployed contracts, run the following command:
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
list known contracts
This should return something like this:
bootstrap5: tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv
bootstrap4: tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv
bootstrap3: tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU
bootstrap2: tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN
bootstrap1: tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx
Note that in the sandboxed mode, some users automatically created so we can easily use them.
It's time to deploy the indice smart contract.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
originate contract indice transferring 1 from bootstrap1 running 'indice.tz' --init '0' --dry-run --burn-cap 0.2
If everything works properly, run the same command without the --dry-run
part.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
originate contract indice transferring 1 from bootstrap1 running 'indice.tz' --init '0' --burn-cap 0.2
The transaction is now launched, and you should have the following response:
Node is boostrapped.
...
New contract KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx originated.
Contract memorized as indice.
Let's open another terminal in order to bake this transaction using the following command line:
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
bake for bootstrap1
Tezos baking is the process of signing and appending a block of transactions to the Tezos blockchain.
Now, run the command below and see your indice contract on the list.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
list known contracts
indice: KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx
bootstrap5: tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv
bootstrap4: tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv
bootstrap3: tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU
bootstrap2: tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN
bootstrap1: tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx
Write down your indice contract address somewhere because you will need it to initialize your advisor contract.
Advisor contract
//advisor.jsligo
#include "advisor_types.jsligo"
const request = (s : advisorStorage) : advisorFullReturn => {
const c_opt : option<contract<unit>> = Tezos.get_entrypoint_opt("%sendValue", s.indiceAddress);
const receiver : contract<unit> =
match(c_opt,{
Some :(c) => c,
None : () => failwith("indice cannot send its value")
});
const op : operation = Tezos.transaction(unit, 0 as mutez, receiver);
const txs : list<operation> = list([op]);
return [txs, s];
};
const execute = (indiceVal : int, s : advisorStorage) : advisorFullReturn => {
return [list([]), {...s, result : s.algorithm(indiceVal)}];
};
const change = (p : advisorAlgo, s : advisorStorage) : advisorFullReturn =>
[ list([]), {...s ,algorithm : p}];
const main = (ep : advisorEntrypoints, store : advisorStorage) : advisorFullReturn =>
match(ep,{
ReceiveValue : (p) => execute(p,store),
RequestValue : () => request(store),
ChangeAlgorithm: (p) => change(p, store)
});
Simulation
Let's simulate the advisor contract with an initial storage of:
{indiceAddress:("KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" : address), algorithm:((i : int) => false), result:false}
.
Note that we choose a simple algorithm function that is ((i : int) => false)
therefore the saved result is false
too.
Let's simulate with ReceiveValue(5)
as an action.
ligo run dry-run advisor.jsligo 'ReceiveValue(5)' '{indiceAddress:("KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" as address), algorithm:((i : int) => false), result:false}'
#output:
#( LIST_EMPTY() ,
# record[algorithm -> "[lambda of type: (lambda int bool) ]" ,
# indiceAddress -> @"KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" ,
# result -> False(unit)] )
Let's simulate again with RequestValue()
as an action.
ligo run dry-run advisor.jsligo 'RequestValue()' '{indiceAddress:("KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" as address), algorithm:((i : int) => false), result:false}'
#output:
#failed with: "indice cannot send its value"
Let's simulate with ChangeAlgorithm((i : int) => (i < 10)?true:false)
as action.
ligo run dry-run advisor.jsligo 'ChangeAlgorithm((i : int) => (i < 10)?true:false)' '{indiceAddress:("KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" as address), algorithm:((i : int) => false), result:false}'
#output:
#( LIST_EMPTY() ,
# record[algorithm -> "[lambda of type: (lambda int bool) ]" ,
# indiceAddress -> @"KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" ,
# result -> False(unit)] )
Compilation
Now, let's prepare the parameters and the storage.
ligo compile parameter advisor.jsligo 'ReceiveValue(5)'
#output:
#(Left (Right 5))
ligo compile parameter advisor.jsligo 'RequestValue()'
#output:
#(Right Unit)
Let's say we want to change the algorithm to a more interesting one while still using a simple business logic: if the indice of a stock (indice storage) is under 20, one should invest.
ligo compile parameter advisor.jsligo 'ChangeAlgorithm((i : int) => (i < 10)?true:false)'
#output:
#(Left (Left { PUSH int 10 ;
# SWAP ;
# COMPARE ;
# LT ;
# IF { PUSH bool True } { PUSH bool False } }))
Let's compile the storage with a similar business logic as an initial state.
ligo compile storage advisor.jsligo '{indiceAddress:("KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx" as address), algorithm:((i : int) => (i < 10)?true:false), result:false}'
#output:
#(Pair (Pair { PUSH bool False ;
# PAIR ;
# { UNPAIR ;
# SWAP ;
# PUSH int 10 ;
# SWAP ;
# COMPARE ;
# LT ;
# IF { DROP ; PUSH bool True } {} } }
# "KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx")
# False)
Once everything looks ok, we can compile the main function and save the output in an advisor.tz
file.
ligo compile contract advisor.jsligo > advisor.tz
The advisor.tz
file should contain:
{ parameter
(or (or (lambda %changeAlgorithm int bool) (int %receiveValue)) (unit %requestValue)) ;
storage
(pair (pair (lambda %algorithm int bool) (address %indiceAddress)) (bool %result)) ;
code { UNPAIR ;
IF_LEFT
{ IF_LEFT
{ DUP 2 ; DIG 2 ; CAR ; DIG 2 ; UPDATE 1 ; UPDATE 1 }
{ DUP 2 ; SWAP ; DIG 2 ; CAR ; CAR ; SWAP ; EXEC ; UPDATE 2 } ;
NIL operation }
{ DROP ;
DUP ;
CAR ;
CDR ;
CONTRACT %sendValue unit ;
IF_NONE { PUSH string "indice cannot send its value" ; FAILWITH } {} ;
PUSH mutez 0 ;
UNIT ;
TRANSFER_TOKENS ;
SWAP ;
NIL operation ;
DIG 2 ;
CONS } ;
PAIR } }
Deployment
As we did for the indice contract, let's run the following command in a --dry-run
mode. In the --init
section we put the Michelson result of the compile storage
that we just ran above.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
originate contract advisor transferring 1 from bootstrap1 running 'advisor.tz' --init '(Pair (Pair { PUSH bool False ; PAIR ; { UNPAIR ; SWAP ; PUSH int 10 ; SWAP ; COMPARE ; LT ; IF { DROP ; PUSH bool True } {} } } "KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx") False)' --dry-run --burn-cap 0.2
If everything works properly, run the same command without the --dry-run
part.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
originate contract advisor transferring 1 from bootstrap1 running 'advisor.tz' --init '(Pair (Pair { PUSH bool False ; PAIR ; { UNPAIR ; SWAP ; PUSH int 10 ; SWAP ; COMPARE ; LT ; IF { DROP ; PUSH bool True } {} } } "KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx") False)' --burn-cap 0.2
The transaction is now launched, and you should have the following response:
New contract KT1N95YD263Fpxjr6vUFFvLX6HrTC4Ds4Q11 originated.
Contract memorized as advisor.
Let's open another initialized terminal in order to bake this transaction using the following command line:
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
bake for bootstrap1
Now, run the command below to see your advisor contract on the list.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
list known contracts
advisor: KT1N95YD263Fpxjr6vUFFvLX6HrTC4Ds4Q11
indice: KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx
bootstrap5: tz1ddb9NMYHZi5UzPdzTZMYQQZoMub195zgv
bootstrap4: tz1b7tUupMgCNw2cCLpKTkSD1NZzB5TkP2sv
bootstrap3: tz1faswCTDciRzE4oJ9jn2Vm2dvjeyA9fUzU
bootstrap2: tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN
bootstrap1: tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx
Use
Now that the two contracts are deployed, we are able to use them. You can run the following command to access the storage of the indicated contract.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
get contract storage for advisor
This should return the same result as the compile storage
command, because no transaction has been made so far.
Pair (Pair { PUSH bool False ;
PAIR ;
{ UNPAIR ;
SWAP ;
PUSH int 10 ;
SWAP ;
COMPARE ;
LT ;
IF { DROP ; PUSH bool True } {} } }
"KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx")
False
Let's check for the indice contract as well.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
get contract storage for indice
#output: 0
Now, let's make a transaction. Imagine the user bootstrap3 wants to use the advisor smart contract to know if he should invest. For that, he must make a transaction using the action RequestValue()
for which the Michelson expression is (Right Unit)
.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
transfer 0 from bootstrap3 to advisor --arg '(Right Unit)'
Then in another terminal, let's bake for bootstrap3,
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
bake for bootstrap3
Now let's check if it is worth investment by looking at the storage.
octez-client \
--base-dir /tmp/mockup \
--mode mockup \
get contract storage for advisor
Pair (Pair { PUSH bool False ;
PAIR ;
{ UNPAIR ;
SWAP ;
PUSH int 10 ;
SWAP ;
COMPARE ;
LT ;
IF { DROP ; PUSH bool True } {} } }
"KT1WibdmFb1TWnTTPyTUNpKF36DD5snpcjmx")
True
The result changes from False
to True
in the result field if it is worth investing. This is the case here because the initial value of the indice storage is still 0
and our algorithm will returns True if this value is under 10
.
Now, it is your turn to play with these two smart contracts by increasing the indice storage or changing the advisor algorithm.
Remember: if you are not sure what you are doing, add
--dry-run
at the end of the command line to see if everything is ok.
To go further, you can find the code with a video explanation of this on our Github.