Welcome to the FunC interactive cheat sheet!

This tutorial is still in work, any contribution is highly appreciated!
Language basics will be covered, more information can be found in the TON repo:

  1. TVM docs
  2. Tests
  3. Sample contracts

Basically, FunC is quite lightweight abstraction on top of TVM assembler, so if you read TVM spec you will likely get the core concepts very quickly.

Contents

🍬 Xeus-Fift specific features

Include macro

In [1]:
#include "stdlib.fc"  ;; searches for file in the FUNCPATH and workdir
Out[1]:

Main-wrapper

There can be three types of cell:

  1. Includes
  2. Function definitions (these functions can be accessed from all other cells)
  3. Non-wrapped code

This non-wrapped code is taken as main entrypoint source and cannot be accessed from other cells.
Consider running a cell as a single invocation of a contract.

Force return

In order to provide a truly interactive experience, xeus-fift inspects the last line of the cell and adds return and trailing semicolon if needed.

Integers

Basic operations

Underlying TVM type: signed 257-bit integers, representing integer numbers in the range -2^256 ... 2^256 − 1

  • Arithmetic + - * / %
  • Bitwise & | << >>
  • Boolean < <= > >= == !=
In [59]:
0x01 ^ 0x80  ;; xor
Out[59]:
129
In [60]:
8 <=> -4  ;; compare (1 if equal, -1 if less, 0 otherwise)
Out[60]:
1
In [210]:
~ 0xff + 0xff  ;; bitwise complement
Out[210]:
-1
In [214]:
min(-2, 0)
Out[214]:
-2
In [216]:
max(2, 3)
Out[216]:
3

Division rounding rules

In [38]:
9 ~>> 2  ;; division by 2^n using nearest-integer rounding
Out[38]:
2
In [39]:
9 ^>> 2  ;; division by 2^n using ceiling rounding
Out[39]:
3

Rounding works similarly for integer division and modular division operators:
~/ ^/ ~% ^%

Compound operators

In [40]:
11 /% 2  ;; extended mod division: computes both the quotient and the remainder
Out[40]:
5 1
In [65]:
divmod(11, 2)  ;; same effect
Out[65]:
5 1
In [66]:
moddiv(11, 2)  ;; change output order
Out[66]:
1 5
In [67]:
int x = 11;  ;; declaring variable of type `int`
x~divmod(2)  ;; non-const method (starting with `~`)
Out[67]:
5 1
In [42]:
int x = 42;
x += 2
Out[42]:
44

Similar let-constructions can be made on top of with practically all integer operators:
-= *= /= %=
&= |= <<= >>= ^=
~>>= ^>>= ~/= ^/= ~%= ^%=

In [104]:
muldiv(3, 3, 2)  ;; equivalent of `a * b / c`
Out[104]:
4
In [105]:
muldivr(3, 3, 2)  ;; equivalent of `a * b ~/ c`
Out[105]:
5

Constants

In [87]:
true
Out[87]:
-1
In [191]:
false
Out[191]:
0
In [43]:
~ false  
    
{-
    Btw, this is a multiline comment
-}
Out[43]:
-1

Tuples

FunC tuples

In [194]:
var t = (1, 2, 3);  ;; packing
t
Out[194]:
1 2 3
In [174]:
var (a, b, c) = (1, 2, 3);  ;; unpacking
b
Out[174]:
2
In [175]:
var (a, b) = (1, (2, 3));
b
Out[175]:
2 3
In [234]:
var (_, x, _) = (0xab, 0xcd, 0xef);  ;; one can use special operand `_` for values that won't be used
x
Out[234]:
205

TVM tuples

An ordered collection of up to 255 components, having arbitrary value types, possibly distinct.
May be used to represent nonpersistent values of arbitrary algebraic data types.

In [176]:
Nil  ;; 0-tuple
Out[176]:
[]

Pairs

In [177]:
pair(0xdead, 0xbeaf)
Out[177]:
[ 57005 48815 ]
In [208]:
second(pair(0xdead, 0xbeaf))
Out[208]:
48815
In [217]:
at(pair(0xdead, 0xbeaf), 0)  ;; universal helper for tuples
Out[217]:
57005
In [179]:
unpair(pair(0xdead, 0xbeaf))
Out[179]:
57005 48815

Triples

In [180]:
triple(1, 2, 3)
Out[180]:
[ 1 2 3 ]
In [202]:
third(triple(1, 2, 3))
Out[202]:
3
In [182]:
untriple(triple(1, 2, 3))
Out[182]:
1 2 3

4-tuples

In [183]:
tuple4(-10, -20, -30, -40)
Out[183]:
[ -10 -20 -30 -40 ]
In [184]:
fourth(tuple4(-10, -20, -30, -40))
Out[184]:
-40
In [185]:
untuple4(tuple4(-10, -20, -30, -40))
Out[185]:
-10 -20 -30 -40

Lists

In [186]:
cons(1, cons(2, cons(3, Nil)))
Out[186]:
[ 1 [ 2 [ 3 [] ] ] ]
In [187]:
uncons(cons(1, cons(2, cons(3, Nil))))  ;; (head, tail)
Out[187]:
1 [ 2 [ 3 [] ] ]
In [188]:
car(cons(1, cons(2, cons(3, Nil))))  ;; head
Out[188]:
1
In [189]:
cdr(cons(1, cons(2, cons(3, Nil))))  ;; tail
Out[189]:
[ 2 [ 3 [] ] ]
In [190]:
list_next(cons(1, cons(2, cons(3, Nil))));  ;; (tail, head)
Out[190]:
[ 2 [ 3 [] ] ] 1

Cells

A TVM cell consists of at most 1023 bits of data, and of at most four references to other cells.
All persistent data (including TVM code) in the TON Blockchain is represented as a collection of TVM cells.

The cell primitives are mostly either cell serialization primitives, which work with Builder s, or cell deserialization primitives, which work with Slices.

Builders

A TVM cell builder, or builder for short, is an “incomplete” cell that supports fast operations of appending bitstrings and cell references at its end.
Builders are used for packing (or serializing) data from the top of the stack into new cells (e.g., before transferring them to persistent storage).

In [255]:
builder b = begin_cell()  ;; two descriptor bytes come first (read more in 3.1.4. Standard cell representation)
Out[255]:
BC{0000}
In [246]:
cell res = end_cell(begin_cell());
Out[246]:
C{96A296D224F285C67BEE93C30F8A309157F0DAA35DC5B87E410B78630A09CFC7}
In [302]:
begin_cell().end_cell()  ;; const method (dot is not a part of the function name)
Out[302]:
C{96A296D224F285C67BEE93C30F8A309157F0DAA35DC5B87E410B78630A09CFC7}
In [281]:
begin_cell().store_int(-0xFF, 32)  ;; we added a 32bit signed integer
Out[281]:
BC{0008ffffff01}
In [283]:
begin_cell().store_uint(-42, 32)
VM error: integer out of range
In [284]:
cell c1 = begin_cell().store_int(1, 8).end_cell();
begin_cell().store_ref(c1)  ;; first byte indicates that cell holds one reference
Out[284]:
BC{0100}

Slices

A TVM cell slice, or slice for short, is a contiguous “sub-cell” of an existing cell, containing some of its bits of data and some of its references.
Essentially, a slice is a read-only view for a subcell of a cell.
Slices are used for unpacking data previously stored (or serialized) in a cell or a tree of cells.

In [307]:
cell c = begin_cell().store_int(0xfa, 16).end_cell();
c.begin_parse()
Out[307]:
CS{Cell{000400fa} bits: 0..16; refs: 0..0}
In [315]:
cell c = begin_cell().store_int(0xfa, 16).end_cell();
slice cs = c.begin_parse();
int res = cs~load_int(16);
res
Out[315]:
250
In [334]:
cell c = begin_cell().store_uint(0xfa, 16).end_cell();
slice cs = c.begin_parse();
var (b1, b2) = (cs~load_uint(16), cs~load_uint(16))  ;; non-const method `load` reads and increases offset
VM error: cell underflow
In [430]:
cell c = begin_cell().store_int(0xfa, 16).end_cell();
slice cs = c.begin_parse();
cs.skip_bits(16).end_parse()  ;; ensure we read the entire cell with `end_parse`
Out[430]:

In [397]:
cell c = begin_cell().store_int(0xfa, 9).end_cell();
slice cs = c.begin_parse();
slice c8 = cs.skip_bits(1).first_bits(8);
c8.preload_uint(8)  ;; read without increasing the offset
Out[397]:
250
In [416]:
cell c1 = begin_cell().store_uint(1, 1).end_cell();
cell c2 = begin_cell().store_ref(c1).end_cell();
cell c3 = c2.begin_parse().preload_ref();
c3.begin_parse().preload_uint(1)
Out[416]:
1

Hashmaps

Hashmaps, or dictionaries, are a specific data structure represented by a tree of cells.

In [294]:
new_dict()
Out[294]:
(null)
In [295]:
dict_empty?(new_dict())
Out[295]:
-1
In [21]:
slice test_slice(int byte) {
    cell c = begin_cell().store_uint(byte, 8).end_cell();
    return c.begin_parse();
}
Out[21]:

Set value

In [22]:
var d = new_dict();
d~udict_set(8, 1, test_slice(0xfa));
d.begin_parse()
Out[22]:
CS{Cell{0005a007ea} bits: 0..22; refs: 0..0}

Replace only if exists

In [37]:
var d = new_dict();
d~udict_set(8, 1, test_slice(0xfa));
var (d1, _) = udict_replace?(d, 8, 1, test_slice(0xfb));
d1.begin_parse()
Out[37]:
CS{Cell{0005a007ee} bits: 0..22; refs: 0..0}
In [38]:
var d = new_dict();
var (d1, found) = udict_replace?(d, 8, 1, test_slice(0xfa));
found
Out[38]:
0

Delete key

In [39]:
var d = new_dict();
d~udict_set(8, 1, test_slice(0xfa));
udict_delete?(d, 8, 1)  ;; returns new dict and boolean flag (found or not)
Out[39]:
(null) -1

Get value

In [36]:
var d = new_dict();
d~udict_set(8, 1, test_slice(0xfa));
udict_get?(d, 8, 1)  ;; returns slice and boolean flag (found or not)
Out[36]:
CS{Cell{0005a007ea} bits: 14..22; refs: 0..0} -1

Functions

Return type

Can be any combination (nested funC tuples) of builtin types int tuple cell slice builder cont, special "hole" type _ (type is deduced from the return value), or void (empty tuple) ()

Modifiers

  • impure - tells the compiler that function can modify passed arguments, and prevents function invocation pruning during the optimization;
  • inline - works pretty the same as in C, tells the compiler to paste function body on every call occurence;
  • inline_ref - in case of a simple inlining, an n-bit slice of the current continuation (cell) is converted to a new continuation (code and codepage), while inline_ref is when a cell reference is converted to a continuation and pushed onto the stack (more info in 4.2.3. Constant, or literal, continuations);
  • method_id - declares a Get method (entrypoint) for the contract (that can be called along with the main, recv_internal and recv_external)

ASM bindings

asm ( stack order of arguments -> stack order of return values ) "INSTRUCTION#0" "INSTRUCTION#1" ... "INSTRUCTION#N" Here is a comprehensive sample function from stdlib demonstrating all possible outcomes:

In [426]:
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
Out[426]:

One can omit both or separate args/return stack ordering:

In [427]:
int dict_empty?(cell c) asm "DICTEMPTY";
Out[427]:

In [428]:
(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF";
Out[428]:

In [429]:
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
Out[429]:

Generics

forall X0, X1, ... , Xn ->

In [425]:
forall X, Y -> tuple pair(X x, Y y) asm "PAIR";
Out[425]:

Conditional statements

In [26]:
if (true) {
    print(1);
} else {
    print(2);
}
Out[26]:
1
In [28]:
ifnot (true) {
    print(3);
} elseif (false) {
    print(4);
} elseifnot (false) {
    print(5);
}
Out[28]:
5

Ternary operator

In [11]:
return 0 == 0 ? 1 : 2
Out[11]:
1

Loops

In [17]:
int i = 10;
while (i > 0) {
    print(i);
    i -= 1;
}
Out[17]:
10 9 8 7 6 5 4 3 2 1
In [20]:
int i = 10;
do {
    print(i);
    i -= 1;
} until (i == 0)
Out[20]:
10 9 8 7 6 5 4 3 2 1
In [25]:
int i = 10;
repeat (5) { i *= 10; }
i
Out[25]:
1000000

Exceptions

In [40]:
throw(31)
VM error: unknown code 31
In [38]:
throw_if(32, true);
VM error: unknown code 32
In [39]:
throw_unless(33, false)
VM error: unknown code 33