Skip to main content
The FunC cookbook was created to consolidate all the knowledge and best practices from experienced FunC developers in one place. The goal is to make it easier for future developers to build smart contracts efficiently. Unlike the rest of the official FunC documentation, this guide focuses on solving everyday challenges that FunC developers encounter during smart contract development.

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 be 0, 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 of number 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:
References:

How to write a do until loop

Use a do..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. The slice_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.
References:

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 the slice_data_empty? function.
References:

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 the slice_refs_empty? function.
References:

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?.
References:

Determine if the data bits of slices are equal

There are three ways to check if the data bits of two slices are equal:
  1. Comparing their hashes.
  2. Using the SDEQ asm instruction.
  3. Using the equal_slice_bits function.
References:

Determine if the cells are equal

Determine whether two cells are equal by comparing their hashes.
References:

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 function preload_bits:
Function 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:
which states that 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.
References:

Data structures

How to determine if a dict is empty

The dict_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.
In 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:

How to store and load a dictionary in permanent storage

The logic for loading a dictionary from local storage is as follows:
Storing the dictionary in local storage is also simple:
References:

How to iterate dictionaries

To iterate a dictionary from the smallest to biggest key, first call a dict_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.
References:

How to delete a value from a dictionary

Use the dict_delete? primitives to delete keys in a dictionary.
References:

How to determine if a tuple is empty

When working with tuples, 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.
The 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:

Basic operations with tuples

The 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:

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.
The 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.
The 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:

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.
The is_int function uses Fift code. Intuitively, the Fift code implements the following FunC-like pseudocode:
Similarly, the is_cell carries out the following FunC-like pseudocode, which makes use of the CTOS TVM instruction:
Finally, the is_slice carries out the following FunC-like pseudocode, which makes use of the SBITS TVM instruction:
References:

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].
The 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:

How to remove an item with a certain index from a tuple

The tlen assembler function uses the TVM instruction TLEN to determine the number of elements in the tuple. References:

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.
For function 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:

Basic operations in lisp-style lists

Lisp-style lists are represented as nested tuples. For example, the list 1, 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:
References:

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 function list-next, which returns the head of the list and the rest of the list. The following snippet illustrates its usage.
References:

How to iterate a cell tree

Each cell can store up to 1023 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.
References:

Contracts

How to determine if the contract state is empty

Consider a smart contract that keeps a counter 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.
References:

How to update the smart contract logic

Below is an example of a simple CounterV1 smart contract that allows the counter to be incremented and includes logic for updating the contract.
After interacting with the contract, and realizing that the functionality for decrementing the counter is missing. Fix this by copying the code from CounterV1 and adding a new decrease function next to the existing increase function. The updated code will look like this:
Once the CounterV2 smart contract is ready, compile it off-chain into a cell and send an upgrade message to the CounterV1 contract:
References:

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 a cell. 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:
The call 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:
Refer to the sending messages page for further details on sending modes in the send_raw_message function. References:

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 same cell 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.
The call 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:
Refer to the sending messages page for further details on sending modes in the send_raw_message function. References:

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.
The function writes the default values for all the message headers, as in the recipe “How to build an internal message with default headers”. 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:
Refer to the sending messages page for further details on sending modes in the send_raw_message function. References:

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 a 0 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.
The function writes the default values for all the message headers, as in the recipe “How to build an internal message with default headers”. 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:
Refer to the sending messages page for further details on sending modes in the send_raw_message function. References:

How to send a message with a long text comment

A cell 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:
The 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:

How to send a deploy message

When sending a deploy message, prepare a StateInit 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.
The function writes the default values for all the message headers, following the same strategy as in the recipe “How to build an internal message with default headers”, with the exception of the last two headers: the StateInit header and the message body header. According to the TL-B for internal messages, the StateInit header satisfies:
which means that the header needs at most two bits, one for deciding the 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.
For further details on all the headers, see the sending messages page and the TL-B for messages. Refer to the sending messages page for further details on sending modes in the send_raw_message function. References:

How to send a message with the entire balance

To transfer the entire balance of a smart contract, use send mode 128. This is particularly useful for proxy contracts that receive payments and forward them to the main contract.
The 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:

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 body in_msg_body contains the message mode, destination address, and the slice to send as body.
The 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:

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 function tpush, 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:
The return type (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:
which will assign the removed element to 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:
Further examples taken from the Standard library:
References:

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.
The function can be called using modifying notation as follows.
Refer to the modifying notation page for further details. References:

Integer utilities

How to get the current time

Use the function now to obtain the current UNIX timestamp.
References:

How to generate a random number

References:

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.
Reference: muldivmod

How to raise a number to a power

To compute n^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.
There is a more efficient way to compute 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.
An usage example of binpow:
References:

How to convert the ASCII digits in a slice into an int

References:

How to convert an int into ASCII digits stored in a slice

References:

Errors

How to throw errors

The following snippet summarizes the main ways of throwing exceptions in FunC, by using functions throw_if, throw_unless and throw.
References:

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’s StateInit, 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:
Therefore, to create an internal address, the above headers must be written in a cell, where 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.
Refer to recipe “How to build a 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:

Generate an external address

Use the TL-B scheme from block to determine the address format to generate an external address.
Since there is a need to find the exact number of bits occupied by the address, declare an asm function with the UBITSIZE opcode. This function will return the minimum number of bits required to store a given number. Reference: TVM instructions