How to write SCaml
SCaml is basically OCaml. If you do not write OCaml, you will not write SCaml either. Learn OCaml first at https://ocaml.org/learn .
There are small tests under
src/tests/ in the repository.
One file per one contract
In SCaml, one contract must be written in one
The API functions to access Michelson primitives are declared in a library module
You should always
open SCaml first. You can omit it but there is almost no point for it.
SCaml.mli to find what available
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]. *) ...
SCaml.mli in this directory or the source code of SCaml
or in the repository
to learn what functions are available and their comments.
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
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.
Accessing a specific entrypoint of a contract is via address literals like
LimitationCurrently there is no SCaml primitive to produce Michelson code
CONTRACT %name t.
An additional small typing phase to enforce the types of each entrypoint to be
'parameter -> 'storage -> (SCaml.operation list, 'storage)
'storage are closed types (without type variables) for the paramter type of the entrypoint and the type of the contract storage respectively.
In SCaml, there are 3 arithmetic types:
- Arbitrary sized integers.
Int (-23). This is not the native
inttype of OCaml but defined in
- Natural numbers
- Arbitrary sized natural numbers.
- It takes a float but internally it is handled as a natural number of micro tezzies.
Tz 0.000001is for 1 mutez. Note that the size is fixed to 64bits (signed) and
Tz 9223372036854.775807is the maximum value for
tz. 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.
Operations over arithmetics are also monomorphic and not overloaded just as OCaml.
- Natural numbers
*^, etc. (
*$, etc. (
SCaml has 4 built-in container types: lists, sets, maps, and big maps.
SCaml provides the APIs
Primitive container literals
Lists, sets, and maps have literals:
[ Int 1; Int 2; Int 3 ]
Set [ Nat 1; Nat 2; Nat 3 ]
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 "0123456789abcdef", Even number of
- Key hashes
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.)
LimitationSCaml does not validate the form of strings for now.
Contract.self returns the contract of the code itself. It has a type
but actually it must agree with the real type of the contract.
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 creation and call
SCaml provides the lowest interface of contract creations and invocations.
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.
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.