How to write SCaml
Learn OCaml
SCaml is basically OCaml. If you do not write OCaml, you will not write SCaml either. Learn OCaml first at https://ocaml.org/learn .
Examples
There are small tests under src/tests/
in the repository.
One file per one contract
In SCaml, one contract must be written in one .ml
file.
Start with open SCaml
The API functions to access Michelson primitives are declared in a library module SCaml
.
You should always open SCaml
first. You can omit it but there is almost no point for it.
Check SCaml.mli
to find what available
Module SCaml
should be found at directory
`opam config var prefix`/lib/scaml
:
$ opam config var prefix
/where-you-install-SCaml/_opam
$ ls /where-you-install-SCaml/_opam/lib/scaml
META SCaml.cmx SCamlib.cma opam
SCaml.cmi SCaml.ml SCamlib.cmxa
SCaml.cmt SCaml.mli SCamlib.cmxs
SCaml.cmti SCamlib.a dune-package
$ cat /where-you-install-SCaml/_opam/lib/scaml/SCaml.mli
...
type ocaml_int = int
type nat = Nat of ocaml_int
(** Arbitrary length nat *)
type int = Int of ocaml_int
(** Arbitrary length int *)
type tz = Tz of float
(** Tezzies. The smallest unit is micro tz, [Tz 0.000001]. *)
...
Check SCaml.mli
in this directory or the source code of SCaml
or in the repository
to learn what functions are available and their comments.
Entrypoint
A Michelson smart contract has one or more entrypoints where its execution starts.
Unless specified explicitly, the last value definition in an .ml
file is considered as the entrypoint of the code.
Multiple entrypoint support
To have more than one entrypoints,
they must be attributed with [@entry]
.
For example:
open SCaml
(* defines an entrypoint "init" *)
let [@entry] init () _ = Int 0
(* defines an entrypoint "do" *)
let [@entry name="do"] do_ () x = x + Int 1
Each entrypoint can optionally take a binding name=<name>
to specify the name of the entrypoint.
If omitted, the name of the defined variable is used for it.
Entrypoint access
Accessing a specific entrypoint of a contract is via address literals like
Address "KT1.....%entrypoint_name"
.
Limitation
Currently there is no SCaml primitive to produce Michelson codeCONTRACT %name t
.
Entrypoint typing
An additional small typing phase to enforce the types of each entrypoint to be
'parameter -> 'storage -> (SCaml.operation list, 'storage)
where 'parameter
and 'storage
are closed types (without type variables) for the paramter type of the entrypoint and the type of the contract storage respectively.
Arithmetics
Arithmetic types
In SCaml, there are 3 arithmetic types:
- Integers
int
- Arbitrary sized integers.
Int 3
,Int (-23)
. This is not the nativeint
type of OCaml but defined inSCaml
. - Natural numbers
nat
- Arbitrary sized natural numbers.
Nat 0
,Nat 12345
. - Tezzies
tz
- It takes a float but internally it is handled as a natural number of micro tezzies.
Tz 0.000001
is for 1 mutez. Note that the size is fixed to 64bits (signed) andTz 9223372036854.775807
is the maximum value fortz
. Any overflow fails the execution of contracts.
There is no overloading of arithmetic constants. Even simple integers must be
explicitly wrapped with its constructor Int
. This is lousy but required for
the simplicitly of the language.
Arithmetic operators
Operations over arithmetics are also monomorphic and not overloaded just as OCaml.
- Integers
+
,-
,*
, etc- Natural numbers
+^
,-^
,*^
, etc. (^
depicts “positive”.)- Tezzies
+$
,-$
,*$
, etc. ($
depicts “currency”.)
Primitive containers
SCaml has 4 built-in container types: lists, sets, maps, and big maps.
Modules List
, Set
, Map
and BigMap
under SCaml
provides the APIs
for them.
Primitive container literals
Lists, sets, and maps have literals:
- Lists
[ Int 1; Int 2; Int 3 ]
- Sets
Set [ Nat 1; Nat 2; Nat 3 ]
- Maps
Map [ (Nat 1, "1"); (Nat 2, "2"); (Nat 3, "3") ]
Currently, all the elements in Set _
and Map _
must be constants.
Big maps have no way of writing literals in Michelson. Neither in SCaml.
Other crypto related literals
- Bytes
Bytes "0123456789abcdef"
, Even number of[0-9a-f]
characters.- Address
Address "tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN"
- Keys
Key "edpkuSR6ywqsk17myFVRcw2eXhVib2MeLc9D1QkEQb98ctWUBwSJpF"
- Key hashes
Key_hash "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"
- Signatures
Signature "edsigu4chLHh7rDAUxHyifHYTJyuS8zybSSFQ5eSXydXD7PWtHXrpeS19ds3hA587p5JNjyJcZbLx8QtemuJBEFkLyzjAhTjjta"
- Timestamps
Timestamp "2019-09-11T08:30:23Z"
, RFC3339 string.
These constructors must take string literals, therefore SCaml has no conversion from string
to these types. (It is impossible in Michelson.)
Limitation
SCaml does not validate the form of strings for now.Self
Contract.self
returns the contract of the code itself. It has a type 'a contract
but actually it must agree with the real type of the contract.
Unlike Michelson's SELF
operator, Contract.self
can appear inside a function.
Even if the function value is sent to another contract, it does not point to the other
contract but to the original contract which uses Contract.self
.
Contract creation and call
SCaml provides the lowest interface of contract creations and invocations.
Contract creation
SCaml provides the lowest level of APIs to originate contracts within SCaml:
- From embeded Michelson codes
Contract.create_from_tz_code <Michelson code string>
takes a string literal of Michelson source code.- From Michelson code file
Contract.create_from_tz_file <Michelson code path name>
takes a string literal of Michelson source file path. The Michelson code in the source file is included at the compilation time. Be careful of setting proper build dependnecy if the Michelson source file is generated from another language.
Contract call
Operation.transfer_tokens
is the only API (so far) to call other contracts within SCaml contracts.
No inter-contract abstractions
SCaml itself will not provide any highly abstracted easy-to-use framework for contract creation and invocation.
There is no trivial standard way for it. For example, we can consider OO approach via classes and objects, and functional approach via functors and modules.
We do not want to fix one of possible approaches in SCaml and push it to its users. Inter-contract frameworks should be built as an SDL with special typing rules which should be compiled down to SCaml.