Chialisp is a high-level, LISP-like language for implementing smart-contract capabilities called puzzles on Chia. Chialisp program compiles into Chialisp Virtual Machine (CLVM). CLVM is serialized and stored directly on the blockchain and is a matter of consensus; it can never be changed. While CLVM powers Chialip, they share many fundamental concepts. Click through the CLVM basic to learn more about CLVM.
If you are new to Chialisp, check out the Chialisp Getting Started Guides first.
There are no variables in Chialisp. Values are stored in two different objects: atoms and cons boxes. A cons box is a pair of objects; the objects in a cons box can either be an atom or another cons box.
An atom is an array of bytes. These bytes can be interpreted both as a signed big-endian integer or a byte string, depending on the operator using it.
All atoms are immutable; therefore, operators that perform computations on atoms create new atoms for the result.
Atoms can be printed in three different ways: decimal, hexadecimal, and string. Hexadecimal values are prefixed by
0x, and strings are quoted in
Cons boxes are represented as parentheses with two elements separated by a
(200 . "hello") ("hello" . ("world" . "!!!"))
A cons box always has two elements. For example, the following is not a valid cons box,
(200 . 300 . 400)
The building blocks of the Chialisp program are lists and operators. A list is any space-separated, ordered group of one or more items inside parenthesis brackets. A strict definition of a list is a representation of consecutive cons boxes terminated in a null atom
Chialisp simplified the representation by allowing omitting implied inner parenthesis brackets in the list. For example, the following expressions are equal:
(200 . (300 . (400 . ()))) (200 300 400)
A Chialisp program is a list of prefix notation form. A valid Chialisp program requires:
- The first item in the list must be a valid operator
- Every item after the first must be a valid program
Take the arithmetic addition operator '+' for example; the list (+ 2 3) computes the sum of integers 2 and 3.
run '(+ 2 3)'5
The arithmetic operators
+, -, *, / and divmod treat their arguments as signed integers.
|It takes any number of integer operands and sums them. If given no arguments, zero is returned.|
|It takes one or more integer operands and adds a0 to the negative of the rest. Giving zero arguments returns 0.|
|It takes any number of integer operands and returns the product.|
|It divides two integers and returns the floored quotient. Rounding for |
|Ittakes two integers and returns a cons-box containing the floored quotient and the remainder.|
Once Chia’s blockchain reaches a height of 2,300,000 (around July 22, 2022), all nodes running version 1.3 or greater will reject all spends which attempt to use the div operator for negative division. Learn more about the change.
|If A is (), return C, otherwise return B. |
if does a lazy evaluation form, so we do not need to worry about the unused code path being evaluated.
$ run '(if 1 (q . "success") (x))' '(100)'"success" $ run '(if 0 (q . "success") (x))' '(100)'FAIL: clvm raise ()
|Returns 1 if A and B are both atoms and A is greater than B, interpreting both as two's complement signed integers. Otherwise (). (> A B) means A > B in infix syntax.|
|Returns 1 if A and B are both atoms and A is greater than B, interpreting both as an array of unsigned bytes. Otherwise (). Compared to strcmp. (>s "a" "b") => ()|
|Returns 1 if A evaluates to (). Otherwise, returns ().|
|Takes an arbitrary number of arguments (even zero). Returns () if any of the arguments evaluate to (). Otherwise, returns 1.|
|Takes an arbitrary number of arguments (even zero). Returns 1 if any of the arguments evaluate to something other than (). Otherwise, returns ().|
list takes any number of parameters and returns them put inside a list.
This saves us from having to manually create nested
(c (A) (c (B) (q ()))) calls, which can get messy quickly.
$ run '(list 100 "test" 0xdeadbeef)'(100 "test" 0xdeadbeef)
The advantages of this may not be immediately obvious but are extremely useful in practice as it allows us to substitute sections of predetermined code.
Suppose we are writing a program that returns another coin's puzzle.
We know that a puzzle takes the form:
(c (c (q . 50) (c (q . 0xpubkey) (c (sha256 2) (q . ())))) (a 5 11))
However, we will want to change 0xpubkey to a value passed to us through our solution.
@ allows us to access the arguments in the higher-level language (
@ == 1)
$ run '(qq (c (c (q . 50) (c (q . (unquote (f @))) (c (sha256 2) ()))) (a 5 11)))' '(0xdeadbeef)' (c (c (q . 50) (c (q . 0xdeadbeef) (c (sha256 2) ()))) (a 5 11))
(mod A B) takes two or more parameters. The first is used to name parameters that are passed in, and the rest are the higher-level script that is to be compiled.
Below we name our arguments
arg_two and then access
arg_one inside our main program.
$ run '(mod (arg_one arg_two) (list arg_one))'(c 2 ())
As you can see, it returns our program in compiled lower level form.
$ brun '(c 2 ())' '(100 200 300)'(100)
You may be wondering what other parameters
mod takes between variable names and source code.
If you want to import some functionality that you frequently use without having to copy/paste it between files, you can use
;; condition_codes.clvm( (defconstant AGG_SIG_ME 50) (defconstant CREATE_COIN 51))
;;main.clvm(mod (pubkey msg puzzle_hash amount) (include "condition_codes.clvm") (list (list AGG_SIG_ME pubkey msg) (list CREATE_COIN puzzle_hash amount)) )
When running main.clvm with
run, make sure to use the
-i option to specify in which directories to look for includable files.
If our condition_codes.clvm file was in the directory
./libraries/chialisp/, then you would pass that to
run so that it knows where to find it:
run -i ./libraries/chialisp/ main.clvm
Also, note that the included files are in a special format. Everything that is defined goes into a single set of parentheses like in condition_codes.clvm above. You can then use any of those constants/functions when writing your program without having to import each one individually. The compiler will only include things that you use, so don't worry about including a large library file when attempting to optimize the size of your program.
In the higher-level language we can define functions, macros, and constants before our program by using
We can define as many of these as we like before the main source code. Usually, a program will be structured like this:
(mod (arg_one arg_two) (defconstant const_name value) (defun function_name (parameter_one parameter_two) *function_code*) (defun another_function (param_one param_two param_three) *function_code*) (defun-inline utility_function (param_one param_two) *function_code*) (defmacro macro_name (param_one param_two) *macro_code*) (main *program*))
A few things to note:
- Functions can reference themselves in their code, but macros and inlines cannot as they are inserted at compile time.
- Both functions and macros can reference other functions, macros, and constants.
- Macros that refer to their parameters must be quasiquoted with the parameters unquoted
- Be careful of infinite loops in macros that reference other macros.
- Comments can be written with semicolons
- Inline functions are generally more cost-effective than regular functions except when reusing calculated arguments:
(defun-inline foo (X) (+ X X)) (foo (* 200 300))will perform the expensive multiplication twice
(mod (arg_one) ; function definitions (defun factorial (input) (if (= input 1) 1 (* (factorial (- input 1)) input)) ) ; main (factorial arg_one))
We can save these files to .clvm files, which can be run from the command line.
Saving the above example as
factorial.clvm allows us to do the following.
$ run factorial.clvm(a (q 2 2 (c 2 (c 5 ()))) (c (q 2 (i (= 5 (q . 1)) (q 1 . 1) (q 18 (a 2 (c 2 (c (- 5 (q . 1)) ()))) 5)) 1) 1)) $ brun '(a (q 2 2 (c 2 (c 5 ()))) (c (q 2 (i (= 5 (q . 1)) (q 1 . 1) (q 18 (a 2 (c 2 (c (- 5 (q . 1)) ()))) 5)) 1) 1))' '(5)'120
Now let's do an example that uses macros as well. When writing a macro, it must be quasiquoted with the parameters being unquoted.
We can also take this time to show another feature of the compiler. You can name each parameter in a list or name the list itself. This works at any place where you name parameters and allows you to handle lists where you aren't sure of the size.
Here we define a macro to square a parameter and then a function to square a list.
(mod (args) (defmacro square (input) (qq (* (unquote input) (unquote input))) ) (defun sqre_list (my_list) (if my_list (c (square (f my_list)) (sqre_list (r my_list))) my_list ) ) (sqre_list args))
Compiling and running this code results in this:
$ run square_list.clvm(a (q 2 2 (c 2 (c 5 ()))) (c (q 2 (i 5 (q 4 (* 9 9) (a 2 (c 2 (c 13 ())))) (q . 5)) 1) 1)) $ brun '(a (q 2 2 (c 2 (c 5 ()))) (c (q 2 (i 5 (q 4 (* 9 9) (a 2 (c 2 (c 13 ())))) (q . 5)) 1) 1))' '((10 9 8 7))'(100 81 64 49)