Statements
How to use flags in if statements
To check whether an event is relevant, use a flag variable of type integer. The flag can either be0, representing false, or -1, representing true.
See absence of boolean type.
When checking the flag in if..else statements, it is unnecessary to use
the == operator, since a 0 evaluates to false, and any nonzero value is
considered to be true in if..else statements.
How to write a repeat loop
A repeat loop helps execute an action a fixed number of times. The example below computes exponentiation ofnumber to the exponent exponent, and illustrates it with specific values number = 2 and exponent = 5:
How to write a while loop
A while loop is useful when the number of iterations is unknown. The following example processes the references in the message cell. Each cell can store up to four references to other cells:- Non-modifying notation using .
begin_cellstore_uintend_cellstore_refbegin_parseslice_refs_empty?load_ref
How to write a do until loop
Use ado..until loop when the loop must execute at least once.
Cells and slices
How to determine if a slice is empty
Before working with a slice, checking whether it contains any data is essential to ensure proper processing. Theslice_empty? method can be used for this purpose. However, it returns 0 (false) if the slice contains at least one bit of data or one reference.
- Non-modifying notation using .
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII stringslice_empty?begin_cellend_cellstore_refbegin_parsestore_slice
How to determine if slice is empty (no bits, but may have refs)
If only the presence of bits matters and the cell references in the slice can be ignored, use theslice_data_empty? function.
- Non-modifying notation using .
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.slice_data_empty?begin_cellend_cellstore_refbegin_parsestore_slice
How to determine if slice is empty (no refs, but may have bits)
If only cell references are of interest, their presence can be checked using theslice_refs_empty? function.
- Non-modifying notation using .
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.slice_refs_empty?begin_cellend_cellstore_refbegin_parsestore_slice
How to determine if a cell is empty
To check whether a cell contains any data, it must first be converted into a slice.- If only the data bits matter, use
slice_data_empty?. - If only cell references matter, use
slice_refs_empty?. - If the presence of any data (bits or cell references) needs to be checked, use
slice_empty?.
- Non-modifying notation using .
begin_cellend_cellstore_refbegin_parseslice_empty?slice_data_empty?slice_refs_empty?
Determine if the data bits of slices are equal
There are three ways to check if the data bits of two slices are equal:- Comparing their hashes.
- Using the SDEQ asm instruction.
- Using the
equal_slice_bitsfunction.
- Non-modifying notation using .
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string."<address string>"acompile-time builtin, where<address string>is a string encoding an address.slice_hashSDEQasm instructionequal_slice_bits
Determine if the cells are equal
Determine whether two cells are equal by comparing their hashes.How to get only the data bits from a slice
If the cell references within a slice are not needed, the raw data bits can be extracted for further processing using the functionpreload_bits:
preload_bits requires as argument the amount of bits to extract.
s.slice_bits() obtains the amount of data bits
in the slice.
References:
How to build a StateInit cell
The code follows the TL-B for StateInit:
code and data should be added as cell references.
Fields split_depth and special are usually set to None (i.e., 0)
in standard programming tasks. The library field usually set to 0 as well.
Data structures
How to determine if a dict is empty
Thedict_empty? function checks whether a dictionary contains any data.
This method is functionally equivalent to cell_null?, as a null cell typically represents an empty dictionary.
d~udict_set(256, 0, "hello"), the function expects unsigned 256-bit integers as keys; also,
d~udict_set(256, 0, "hello") will mutate the dictionary d, since udict_set is called using
modifying notation with the symbol ~.
References:
- Non-modifying notation using .
- Modifying notation using ~
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.new_dictdict_setprimitivesdict_empty?
How to store and load a dictionary in permanent storage
The logic for loading a dictionary from local storage is as follows:- Non-modifying notation using .
- Modifying notation using ~
- Slice primitives
get_dataset_data- Builder primitives
new_dictslice_empty?load_dict
How to iterate dictionaries
To iterate a dictionary from the smallest to biggest key, first call adict_get_min? primitive to obtain the smallest key in the dictionary,
and then call a dict_get_next? primitive inside a loop while checking a flag for existence of further key-value pairs to process.
Similarly, to iterate a dictionary from the biggest to smallest key, first call a dict_get_max? primitive to obtain the biggest key in the dictionary,
and then call a dict_get_prev? primitive inside a loop while checking a flag for existence of further key-value pairs to process.
- Non-modifying notation using .
- Modifying notation using ~
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.new_dictdict_setprimitivesdict_get_min?primitivesdict_get_max?primitivesdict_get_next?primitivesdict_get_prev?primitives
How to delete a value from a dictionary
Use thedict_delete? primitives to delete keys in a dictionary.
"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.- Non-modifying notation using .
- Modifying notation using ~
new_dictdict_setprimitivesdict_delete?primitivesdict_get?primitives
How to determine if a tuple is empty
When working withtuples, checking for existing values before extracting them is crucial.
Extracting a value from an empty tuple will result in an error: “not a tuple of valid size” - exit code 7.
tlen assembler function uses
the TVM instruction TLEN to determine the number of elements in the tuple.
The tpush function appends an element to the tuple, so that it becomes the last element.
References:
- Non-modifying notation using .
- Modifying notation using ~
- Assembler functions
empty_tupletpush- TVM Exit codes
- TVM instructions
Basic operations with tuples
tlen assembler function uses the TVM instruction TLEN to determine the number of elements in the tuple.
The tpop assembler function uses the TVM instruction TPOP to detach the last element from the tuple, and
it returns the mutated tuple and the detached element.
References:
- Non-modifying notation using .
- Modifying notation using ~
- Assembler functions
- Polymorphic functions
empty_tupletpushfirstat- TVM instructions
How to iterate tuples (both directions)
When working with arrays or stacks in FunC, tuples are essential. The first step is learning how to iterate through tuple values for processing.tlen assembler function uses the TVM instruction TLEN to determine the number of elements in the tuple.
The to_tuple casts any type into an arbitrary length tuple, which leads to run-time errors if to_tuple is used to cast non-tuple types.
Be careful to only cast fixed-length tuples, like [1, 2]. The to_tuple is essentially a dummy function that does nothing,
because it uses the No operation NOP instruction. The only purpose of to_tuple is to tell the type-checker
to accept the input to to_tuple as a tuple.
References:
Iterating n-nested tuples
Sometimes, while traversing the elements of a tuple, there is the need to iterate through nested tuples. The following example iterates through a tuple starting from the last index, and finds the biggest number, irrespective if there are nested tuples. For example, in the tuple[[2,6],[1,[3,[3,5]]], 3], the example finds 6 as the biggest number.
tuple_length assembler function uses the TVM instruction TLEN to determine the number of elements in the tuple.
The tpop assembler function uses the TVM instruction TPOP to detach the last element from the tuple, and
it returns the mutated tuple and the detached element.
The is_tuple assembler function uses the TVM instruction ISTUPLE to determine if the argument is a tuple or not.
The to_tuple and to_int cast any type into an arbitrary length tuple and integer, respectively. This leads to run-time errors if
to_tuple and to_int are used to cast non-tuple and non-integer types, respectively. The to_tuple and to_int are essentially dummy functions that do nothing,
because they use the No operation NOP instruction. The only purpose of to_tuple and to_int is to tell the type-checker
to accept the input to to_tuple as a tuple, and the input to to_int as an int.
References:
- Non-modifying notation using .
- Modifying notation using ~
- Assembler functions
- Polymorphic functions
- Global Variables
- TVM instructions
Casting types in tuples
If a tuple contains various data types[cell, slice, int, tuple, ...], there is the need to check the value and cast it accordingly before processing.
The following snippet illustrates this idea.
is_int function uses Fift code. Intuitively, the Fift code implements the
following FunC-like pseudocode:
is_cell carries out the following FunC-like pseudocode, which makes use
of the CTOS TVM instruction:
is_slice carries out the following FunC-like pseudocode, which makes use
of the SBITS TVM instruction:
- Modifying notation using ~
- Assembler functions
- Polymorphic functions
empty_tupletpush- TVM instructions
Reversing tuples
The following example reverses any tuple. For example, given the input[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
the reverse_tuple produces the output [10, 9, 8, 7, 6, 5, 4, 3, 2, 1].
tuple_length assembler function uses the TVM instruction TLEN to determine the number of elements in the tuple.
The tpop assembler function uses the TVM instruction TPOP to detach the last element from the tuple, and
it returns the mutated tuple and the detached element.
The to_tuple casts any type into an arbitrary length tuple, which leads to run-time errors if to_tuple is used to cast non-tuple types.
The to_tuple is essentially a dummy function that does nothing,
because it uses the No operation NOP instruction. The only purpose of to_tuple is to tell the type-checker
to accept the input to to_tuple as a tuple.
References:
- Non-modifying notation using .
- Modifying notation using ~
- Assembler functions
- Polymorphic functions
empty_tupletpush- TVM instructions
How to remove an item with a certain index from a tuple
tlen assembler function uses the TVM instruction TLEN to determine the number of elements in the tuple.
References:
- Non-modifying notation using .
- Modifying notation using ~
- Assembler functions
empty_tupleattpush- TVM instructions
Determine if tuples are equal
The approach involves iterating through both tuples and comparing each value recursively. Since tuples can contain different data types, check types and cast values dynamically.are_slices_equal? refer to recipe “Determine if the data bits of slices are equal”.
For function are_cells_equal? refer to recipe “Determine if the cells are equal”.
For explanation of the functions that check types, refer to the recipe “Casting types in tuples”.
References:
- Non-modifying notation using .
- Modifying notation using ~
- Assembler functions
- Polymorphic functions
tpush- TVM instructions
Basic operations in lisp-style lists
Lisp-style lists are represented as nested tuples. For example, the list1, 2, 3 is represented as the
nested tuple [1, [2, [3, null]]], where the value null acts as a marker for the end of the list.
Use the cons function to add an element at the front of the provided list. For example,
if lst is the list 2, 3, then cons(1, lst) is the list 1, 2, 3. Internally, cons(1, lst) builds the tuple [1, lst], where
lst is the tuple [2, [3, null]].
The null? function checks if the provided argument is the null value, or equivalently, the empty list.
The following snippet illustrates these functions:
How to iterate through a lisp-style list
As described in the recipe “Basic operations in lisp-style lists”, Lisp-style lists are represented as nested tuples. It is possible to iterate a lisp-style list by using the functionlist-next,
which returns the head of the list and the rest of the list. The following snippet illustrates its usage.
- Lisp-style lists
- Non-modifying notation using .
- Modifying notation using ~
consnullnull?list_next
How to iterate a cell tree
Each cell can store up to1023 bits of data and 4 cell references. Therefore, it is possible to represent
complex tree structures by linking cells using cell references.
Given a tree of cells, use any tree traversal algorithm
to access each cell in the tree. For example, the snippet below uses the
iterative version of pre-order traversal,
which makes use of a stack, instead of recursive calls.
The stack is implemented using the same technique for lisp-style lists.
The only difference is that the list grows by appending elements to the end of the list, instead of at the front.
The last element in the list is the top of the stack.
More concretely, if s is the current stack, append an element x by constructing the tuple [s, x].
This tuple [s, x] is the new stack, with top element x.
For example, the stack 1, 2, 3, where 3 is the top element, is represented using nested tuples
as [[[null, 1], 2], 3], where null represents the empty stack.
- Non-modifying notation using .
- Modifying notation using ~
- Builder primitives
- Slice primitives
Lisp-style lists- Assembler functions
nullnull?slice_refs- TVM instructions
Contracts
How to determine if the contract state is empty
Consider a smart contract that keeps acounter stored in its state, that tracks the number of internal messages the contract has received.
When the contract receives its first message, the contract state is empty, which means that the counter has not been initialized yet.
It is important to handle all scenarios by checking if the state is empty and initializing the counter accordingly.
- Non-modifying notation using .
- Modifying notation using ~
recv_internalmethodget_databegin_parseslice_empty?begin_cellend_cellstore_uintload_uintset_data
How to update the smart contract logic
Below is an example of a simpleCounterV1 smart contract that allows the counter to be incremented and includes logic for updating the contract.
CounterV1 and adding a new decrease function next to the existing increase function. The updated code will look like this:
CounterV2 smart contract is ready, compile it off-chain into a cell and send an upgrade message to the CounterV1 contract:
Messages
How to build an internal message with default headers
When a smart contract needs to send an internal message, it must first construct the message as acell. This includes specifying technical flags, the recipient’s address, and additional data.
The most common case involves sending an internal message that is bounceable, with no StateInit, and with message body serialized in the same message cell. The following build_message function illustrates this common case. The function receives as parameters the destination address dest_addr encoded as a slice, the amount in nanotons to send amount, and the requested operation opcode opcode:
store_uint(0x18, 1 + 1 + 1 + 1 + 2) sets 5 headers to their default values. The sum 1 + 1 + 1 + 1 + 2
represents the number of bits occupied by each header, i.e., the first header occupies 1 bit, the second header 1 bit, and so on
until the 5th header which occupies 2 bits. The hexadecimal number 0x18 is a shorthand for the 6 bits 011000, which represents
the default values for each of the headers, i.e., the first header has value 0, the second header 1, and so on until the
5th header, which has the two bits 00.
Among these 5 headers, the third one is probably the most interesting for a programmer, because it corresponds to the bounceable flag,
which is set to 1 (true) by default. If the flag is required to be 0 (false), use hexadecimal 0x10 instead of 0x18,
because 0x10 corresponds to the 6 bits 010000.
The call store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) sets 7 further headers to the default value 0. The sum 1 + 4 + 4 + 64 + 32 + 1 + 1 represents the number of bits occupied by each header. Among these 7 headers, the last two are probably the most interesting for a programmer, because they correspond to the StateInit header and the message body ref header, respectively. In particular, the default headers state that the message has no StateInit, and that the message body is not stored as a cell reference, but directly in the cell, together with the headers. Refer to recipes “How to send a deploy message” and “How to set the message body as a ref in an internal message” for examples on how to manipulate StateInit and the message body ref headers, respectively.
For further details on all the headers, see the sending messages page and
the TL-B for messages.
Here is an example on how to use function build_message to send a message:
send_raw_message function.
References:
- Non-modifying notation using .
begin_cellstore_uintstore_slicestore_coinsend_cell- TL-B for messages
- Sending messages page
"<address string>"acompile-time builtin, where<address string>is a string encoding an address.send_raw_message
How to set the message body as a ref in an internal message
If there is sufficient space, the message body can be stored in the samecell together with the message headers,
as shown in the recipe “How to build an internal message with default headers”.
If there is not enough space in the message cell, the message body can be stored as a cell reference to the message, as shown in the following
function. The function receives the destination address, the amount to send, and the message body as a separate cell.
store_uint(0x18, 1 + 1 + 1 + 1 + 2) sets 5 headers to their default values, as in the recipe
“How to build an internal message with default headers”.
The call store_uint(0, 1 + 4 + 4 + 64 + 32 + 1) sets 6 further headers to the default value 0, as in
the first 6 headers in recipe “How to build an internal message with default headers”.
The last header, corresponding to the message body ref header, is set with the call store_uint(1, 1), which indicates that the
message body will be included as a cell reference.
For further details on all the headers, see the sending messages page and
the TL-B for messages.
Here is an example on how to use function build_message to send a message:
send_raw_message function.
References:
- Non-modifying notation using .
begin_cellstore_uintstore_slicestore_coinsend_cell- TL-B for messages
- Sending messages page
"<address string>"acompile-time builtin, where<address string>is a string encoding an address.send_raw_message
How to set the message body as a slice in an internal message
If the message body needs to be included directly in the message cell, but the message body is already in a separate slice, write the slice into the message cell, as in the following function. The function receives the destination address, the amount to send, and the message body as a slice.build_message to send a message:
send_raw_message function.
References:
- Non-modifying notation using .
begin_cellstore_uintstore_slicestore_coinsend_cellbegin_parse- TL-B for messages
- Sending messages page
"<address string>"acompile-time builtin, where<address string>is a string encoding an address.send_raw_message
How to send a message containing a comment
A “comment” is an ASCII string encoded as a slice. To send a message with a comment, write a0 opcode followed by the comment, as done in the following function.
The function receives the destination address, the amount to send, and the comment encoded as a slice.
build_message to send a message:
send_raw_message function.
References:
- Non-modifying notation using .
begin_cellstore_uintstore_slicestore_coinsend_cell- TL-B for messages
- Sending messages page
"<address string>"acompile-time builtin, where<address string>is a string encoding an address."<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.send_raw_message
How to send a message with a long text comment
Acell can store up to 1023 bits of data, which means up to 127 8-bit characters.
If there is a need to send a message with a really long comment, split the comment into several slices.
Each slice should have at most 127 chars. Each slice should have a reference to the next one,
forming a snake-like structure.
The following example illustrates the idea:
build_message function is exactly the function used in the
recipe How to set the message body as a ref in an internal message.
Refer to the sending messages page for further details on sending modes in the send_raw_message function.
References:
- Non-modifying notation using .
begin_cellstore_uintstore_slicestore_refend_cell"<address string>"acompile-time builtin, where<address string>is a string encoding an address."<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.send_raw_message- Sending messages page
How to send a deploy message
When sending a deploy message, prepare aStateInit cell, as done in the recipe How to build a StateInit cell. Once the StateInit cell is ready, prepare a message cell in which the message body is included with the headers, or the message body is included as a separate cell.
The following function illustrates the case for the message body included in the same cell as the headers. The function receives the destination address, the amount to send, the StateInit cell, and the message body as a slice.
StateInit header and the message body header.
According to the TL-B for internal messages, the StateInit header satisfies:
Maybe and, in case the Maybe bit is active, another for deciding the Either. In the function above, the StateInit header was set to the 2-bit integer 3, which corresponds to the binary 11. The first bit corresponds to the Maybe, and since it is active, it signals that there is a StateInit in the message. The second bit corresponds to the Either, and since it is active, it signals that the right branch of the Either was chosen, i.e., the branch ^StateInit, which means that the StateInit is included as a reference cell in the message.
The message body header was set to the 1-bit value 0, which means that the message body in included in the same cell together with the headers.
As a second example, the following function also includes a StateInit, but the message body is included as a separate cell. The function receives the destination address, the amount to send, the StateInit cell, and the message body as a cell.
send_raw_message function.
References:
- Non-modifying notation using .
begin_cellstore_uintstore_slicestore_coinsstore_refend_cellsend_raw_message- TL-B for messages
- Sending messages page
How to send a message with the entire balance
To transfer the entire balance of a smart contract, use send mode128.
This is particularly useful for proxy contracts that receive payments and forward them to the main contract.
build_message function is exactly the function used in the
recipe How to send a message containing a comment.
Refer to the sending messages page for further details on sending modes in the send_raw_message function.
References:
"<address string>"acompile-time builtin, where<address string>is a string encoding an address."<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.- Sending messages page
send_raw_message
How to send a message in a proxy contract
A proxy contract facilitates message exchange between a user and a main contract. The proxy contract redirects messages based on the parameters it receives in the incoming message. For example, this is a simple example of a proxy contract. It expects that the incoming message bodyin_msg_body contains the message mode,
destination address, and the slice to send as body.
build_message function is exactly the function used in the
recipe How to set the message body as a slice in an internal message.
Refer to the sending messages page for further details on sending modes in the send_raw_message function.
References:
- Modifying notation using ~
- load_uint
load_msg_addr- Sending messages page
send_raw_message
Functions
How to write custom functions using asm keyword
Many features in FunC come from predefined methods in the Standard library. However, there are many functionalities that the standard library does not cover, but are available as TVM instructions. In such cases, it is possible to define functions that make use of the TVM instructions. For example, while the functiontpush, which adds an element to the end of a tuple, exists in the standard library,
there is no tpop function, which removes the last element in a tuple and returns the modified tuple and the removed element.
But there is a TVM instruction TPOP that does precisely this.
So, define the function tpop as an assembler function that wraps the TPOP instruction:
(tuple, X) indicates that the function produces the modified tuple and the extracted element as a result.
The function is polymorphic in the sense that the type of the returned element X
can be any type. The function name uses the symbol ~ to indicate that this function can be called
using modifying notation.
For example, if it is certain that tuple t has only integer elements, call the function using
modifying notation, like this:
elem and also modify tuple t implicitly.
As another example, the following function determines the length of a tuple by wrapping the TLEN TVM instruction:
How to use modifying notation on functions
To use modifying notation on a function, define the function so that it has a type of the form(A, ...) -> (A, B), for arbitrary type B. Functions of this type usually mutate their first
argument and return the mutated argument as their first result.
For example, a function f of type (slice, int, slice) -> (slice, cell), can be called using modifying notation as
cell result = s~f(0, "hello"), where s is some slice. The modifying notation is a shorthand for the standard function call
(s, cell result) = f(s, 0, "hello"), where s is reassigned after the call to f.
For a more concrete example, the following defines a function that reads a digit from a slice that stores ASCII digits.
The function receives the slice as an argument and produces two results. The first result is the modified slice,
so that it is ready to read the next digit.
The second result is the loaded digit.
- Modifying notation using ~
load_uint"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.
Integer utilities
How to get the current time
Use the functionnow to obtain the current UNIX timestamp.
How to generate a random number
Modulo operations
As an example, let’s say there is a need to perform the following calculation for all 256 numbers:(xp + zp) * (xp - zp).
Since these operations are commonly used in cryptography, modulo operator for montgomery curves should be used.
Note:
Variable names like xp+zp are valid as long as there are no spaces between the operators.
muldivmod
How to raise a number to a power
To computen^e, the naive approach multiplies e - 1 times the number n by itself. For example, n^3
means to multiply by n 2 times: (n * n) * n.
The following code implements such idea. It has a complexity linear on e.
n^e called binary exponentiation.
It has a complexity base-2 logarithmic on e, i.e., O(log_2 e).
This is the recursive implementation of the algorithm. Refer to the article for details.
binpow:
How to convert the ASCII digits in a slice into an int
- Modifying notation using ~
- Non-modifying notation using .
load_uint"<ascii string>"compile-time builtin, where<ascii string>is an ASCII string.
How to convert an int into ASCII digits stored in a slice
- Modifying notation using ~
- Non-modifying notation using .
begin_cellend_cellbegin_parsenulldivmodstore_uint- List-style lists
Errors
How to throw errors
The following snippet summarizes the main ways of throwing exceptions in FunC, by using functionsthrow_if, throw_unless and throw.
Addresses
Generate an internal address
When deploying a new contract, there is the need to generate its internal address because it is initially unknown. The internal address can be generated from the contract’sStateInit, which contains the code and data of the new contract.
According to the MsgAddressInt TL-B schema, an internal address is composed of the following headers:
address header is computed using the hash of the contract’s StateInit. The following function creates an internal address that follows these headers, and stores the address in a cell, which finally gets transformed into a slice, as slices tend to be the preferred format for storing internal addresses.
StateInit cell”. Additionally, refer to recipe “How to send a deploy message” for sending a deploy message.
Further information on workchain IDs are found in the docs.
References:
- Modifying notation using ~
- Non-modifying notation using .
begin_cellend_cellbegin_parsecell_hash()store_uintstore_int- Workchain ID
- Internal addresses
Generate an external address
Use the TL-B scheme from block to determine the address format to generate an external address.UBITSIZE opcode. This function will return the minimum number of bits required to store a given number.
Reference: TVM instructions