Hello World In Scilla
“Scilla imposes a structure on smart contracts that will make applications less vulnerable to attacks by eliminating certain known vulnerabilities directly at the language-level. Furthermore, the principled structure of Scilla will make applications inherently more secure and amenable to formal verification.”
Here is the Hello World contract. I encourage you to continually flip back and forth or split screen the contract, on the ide or github, as you work through this post.
(* HelloWorld contract *)
(***************************************************)
(* Scilla version *)
(***************************************************)
scilla_version 1
(***************************************************)
(* Associated library *)
(***************************************************)
library HelloWorld
let one_msg =
fun (msg : Message) =>
let nil_msg = Nil {Message} in
Cons {Message} msg nil_msg
let not_owner_code = Uint32 1
let set_hello_code = Uint32 2
(***************************************************)
(* The contract definition *)
(***************************************************)
contract HelloWorld
(owner: ByStr20)
field welcome_msg : String = ""
transition setHello (msg : String)
is_owner = builtin eq owner _sender;
match is_owner with
| False =>
msg = {_tag : "Main"; _recipient : _sender; _amount : 0; code : not_owner_code};
msgs = one_msg msg;
send msgs
| True =>
welcome_msg := msg;
msg = {_tag : "Main"; _recipient : _sender; _amount : 0; code : set_hello_code};
msgs = one_msg msg;
send msgs
end
end
transition getHello ()
r <- welcome_msg;
e = {_eventname: "getHello"; msg: r};
event e
end
It’s a lot to take in.
for now, I am going to ignore the library section at the top and jump ahead to this on line 8.
contract HelloWorld
This defines and names the contract.
(Im)mutable Variables
Scilla has two types of variable. Immutable and mutable variable.
First we worry about the immutable variables which will always immediately follow naming the contract.
In our Hello World contract we want to check that only the owner of the contract can use the contract. So we declare an immutable variable like this.
(owner : ByStr20)
Immutable variable are structured like this ([name] : [type]).
Mutable Variables
When doing operations on data, we need to have variables whose value will be changed by the code in the contract.
These variables, which can be updated, are still stored on the blockchain and are known as the mutable variables.
To declare a mutable variable, we need to pay attention to three things:- Variable name: This is the identifier of the variable to be used by various operators later in the contract. Variable type: Choose the appropriate type of the variable. Such as
Uint128
for amounts, String for names,ByStr20
for addresses etc. Variable value: We may choose to declare a variable with or without an initial value. https://github.com/noelyoo/learn-scilla
The mutable variable looks like this
field welcome_msg : String = ""//structurefield [Variable Name] : [Variable Type] = “[Variable Text]”
- Field is the mutable variable keyword
- welcome_msg is the name of our mutable variable
- String is the type
- At welcome_msg there is an empty string
The Contract So Far
contract HelloWorld
(owner: ByStr20)
field welcome_msg : String = ""
Transition
Now we need to set this mutable variable. This is done with a transition. Think of a transition like a function, and you will do just fine.
transition setHello (msg : String) //structure[keyword] [Name] (parameter : type)
It’s very important to make sure only the owner of this contract (who we set as our immutable variable) is the only one able to call the setHello function (I mean transition)
Builtin?
Take a look at this next bit of code.
is_owner = builtin eq owner _sender;----------------Structure------------------[variable] = [keyword] [equivalence] [A] [B]// is_owner = A === B
builtin is a keyword that lets you use the builtin Scilla functionality. The functionality being used here is eq.
eq is a function that equates owner with _sender and returns either true or false. Similar to == or ===
_sender?
Every transition has access to _sender. It is the address of the account which called the transition.
is_owner = builtin eq owner _sender;
This sets is_owner to true or false depending on if the contract is owned by the account that’s calling the transition.
Match This Do That
now for a chunk
match is_owner with
| False =>
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};
msgs = one_msg msg;
send msgs
| True =>
welcome_msg := msg;
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};
msgs = one_msg msg;
send msgs
end
end
Bit by bit
match is_owner with | False =>
A JavaScript Comparison
let is_owner = true || false;switch (is_owner) {
case true:
//Do something
break;
case false:
//Do something else;
break;
}
The match operator makes a comparison. If a condition is matched, it does whatever comes after this =>
What are we doing after this => ? It depends on if we take the false or the true path. If is_sender = false we do this.
False => msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};
WTF?
You can’t understand what is happening up there, without first understanding what’s happening down here.
msgs = one_msg msg;
send msgs
- one_msg is a utility function that allows you to create a list of messages and inserts msg into the list. (it’s defined above in the library)
- This list of type messages are all stored in a variable named msgs
- The messages are then sent
What Is A Message?
A message is a type. An outgoing message contains information
All messages need to have_tag
, _recipient
and _amount
entries. If you leave them out, it will pass the type checker but when you try call the transition in you contract you will get this error.
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};
What does this all mean?
- {_tag : “Main”; is old syntax and no longer necessary. Try using an empty string instead, and it will work fine.
- _recipient : _sender; means that the sender is the recipient of this message.
- _amount : Uint128 0; means that nothing is being sent.
- code : not_owner_code} it’s defined up top in the library, but this either returns a 1 or 2.
The Library
library HelloWorldlet one_msg =
fun (msg : Message) =>
let nil_msg = Nil {Message} in
Cons {Message} msg nil_msglet not_owner_code = Int32 1
let set_hello_code = Int32 2
This is where the one_msg function is declared. It’s up in the library. So what is happening here?
fun is a keyword (arguments : type) => (*This is what happens*)
Nil
creates an emptyList
. It takes the following form:Nil {A}
, is an empty list of type {A}. Nil {Message}- What is
in? in
is not a separate keyword. Consider this,let v = expr1 in expr2
. This is the way in OCaml to define a "local" variable. What it's saying is that you're going to usev
as a named value inexpr2
, and its value when it appears inexpr2
is the value ofexpr1
. in
can also mean the same thing as a semicolon does in JavaScript or Python. It’s common syntax in functional programming.
let x = int32 1 in // is pretty much the same as saying
let x = 1;
Cons
adds an element to an existing list. It takes the following form:Cons {'A} h l
, where'A
is a type variable that can be instantiated with any type andh
is an element of type'A
that is inserted at the head of listl
(of typeList 'A
).
I think that should clear up the one_msg function. It creates a list and then adds the transition argument to the list before sending it.
msgs = one_msg msg;
send msgs
The Final Transition
transition getHello ()
r <- welcome_msg;
e = {_eventname: "GetHello"; msg: r};
event e
end
Notice we are using an event here rather than send.
It’s also possible to write this transition like this with send
transition getHello ()
r <- welcome_msg;
msg = {_tag : “Main”; _recipient : _sender; _amount : Uint128 0; msg : r};
msgs = one_msg msg;
send msgs
end
In Scilla, there are two ways that transitions can transmit data. One way is through send messages and another way is through events.
send is used to send message to another smart contract and is not emitted back to your client application. Meanwhile, events are dispatched signals that smart contracts can use to transmit data to client applications.
r <- welcome_msg;
This takes the value in welcome_msg and copies it into r.
e = {_eventname : "Gethello"; msg : r};
The first part {_eventname : “Gethello”;} names the event. The eventname is compulsory. You can call the event whatever you want. “Gethello” can be “Leavegoodbye” it doesn’t make a difference. Next there is a ; followed by another name : value pair. msg is the name and r is the value. r of course, contains the value of welcome_msg.
finally
event e
emits the event called e.