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.

Basics

How to write an if statement

Let’s say we want to check if any event is relevant. To do this, we use the flag variable. Remember that in FunC true is -1 and false is 0. To check whether an event is relevant, use a flag variable. In FunC, true is represented by -1, and false is 0.
Note: The == operator is unnecessary, as 0 already evaluates to false, and any nonzero value is considered true. Reference: If statement in docs

How to write a repeat loop

A repeat loop helps execute an action a fixed number of times. The example below demonstrates exponentiation:
Reference: Repeat loop in docs

How to write a while loop

A while loop is useful when the number of iterations is unknown. The following example processes a cell which 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.
Reference: Until loop in docs

How to determine if 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 ref.
References:

How to determine if slice is empty (no bits, but may have refs)

If only the presence of bits matters and refs in slice can be ignored, use the slice_data_empty?().
References:

How to determine if slice is empty (no refs, but may have bits)

In case we are only interested in refs, we should check their presence using slice_refs_empty?(). If only refs are of interest, their presence can be checked using the slice_refs_empty?().
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 bits matter, use slice_data_empty?().
  • If only refs matter, use slice_refs_empty?().
  • If the presence of any data (bits or refs) needs to be checked, use slice_empty?().
References:

How to determine if a dict is empty

The dict_empty?() checks whether a dictionary contains any data. This method is functionally equivalent to cell_null?(), as a null cell typically represents an empty 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.
Note: We are defining the tlen assembly function. You can find more details here and a see a list of assembler commands. References:

How to determine if a lisp-style list is empty

We can use the cons function to add an element to determine if a lisp-style list is empty. For example, adding 100 to the list ensures it is not empty.

How to determine a state of the contract is empty

Consider a smart contract with a counter that tracks the number of transactions. This variable does not exist in the contract state during the first transaction because it is empty. It is important to handle this scenario by checking if the state is empty and initializing the counter accordingly.
Note: The contract state can be determined as empty by verifying whether the cell is empty. References:

How to build an internal message cell

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.
Note:
  • In this example, we use the literal a to obtain an address. More details on string literals can be found in the documentation.
  • You can find more details in the documentation. A direct link to the layout is also available.
References:

How to contain a body as a ref in an internal message cell

The message body can contain int, slices, or cells following flags and other technical data. If a cell is used, a bit must be set to 1 before calling store_ref(), indicating that the cell will be included. Alternatively, if there is sufficient space, the message body can be stored in the same cell as the header. In this case, the bit should be set to 0.
Note:
  • In this example, we use the literal a to obtain an address. More details on string literals can be found in the documentation.
  • The example uses mode 3, which ensures the contract deducts the specified amount while covering the transaction fee from the contract balance and ignoring errors.
    • mode 64 returns all received tokens, subtracting the commission.
    • mode 128 transfers the entire balance.
  • The message is constructed with the body added separately.
References:

How to contain a body as a slice in an internal message cell

A message body can be sent as either a cell or a slice. In this example, the body is sent inside a slice.
Note:
  • The literal a is used to obtain an address. See the documentation for details on string literals.
  • The example uses mode 3, mode 64, and mode 128, as described above.
  • The message is constructed with the body included as a slice.

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.
Note:
  • The tlen assembly function is declared here. You can read more about it and explore a list of all assembler commands.
  • The to_tuple function is also declared. This function converts any input into a tuple, so use it carefully.

How to write custom functions using asm keyword

Many features we use in FunC come from pre-prepared methods inside stdlib.fc. However, we have many more capabilities, and learning to write custom functions unlocks new possibilities. For example, while tpush, which adds an element to a tuple, exists, there is no built-in tpop function. In such cases, we must implement it ourselves.
We must determine its length if we want to iterate over a tuple. We can achieve this by writing a new function using the TLEN asm instruction.
Examples of functions from stdlib.fc:
References:

Iterating n-nested tuples

Sometimes, we need to iterate through nested tuples. The following example iterates through a tuple formatted as: [[2,6],[1,[3,[3,5]]], 3] starting from the head.
References:

Basic operations with tuples

Resolving type X

If a tuple contains various data types X (cell, slice, int, tuple, etc.), we may need to check the value and cast it accordingly before processing.
Reference: TVM instructions in docs

How to get current time

How to generate a random number

Modulo operations

As an example, let’s say we need to perform the following calculation for all 256 numbers: (xp + zp) * (xp - zp). Since these operations are commonly used in cryptography, we utilize the modulo operator for montgomery curves. Note: Variable names like xp+zp are valid as long as there are no spaces between the operators.
Reference: muldivmod in docs

How to throw errors

Standard TVM exception codes

Reversing tuples

Since tuples behave as stacks in FunC, sometimes we need to reverse them to access data from the opposite end.
Reference: tpush() in docs

How to remove an item with a certain index from the list

Determine if the slices are equal

There are two main ways to check if two slices are equal:
  • Comparing their hashes.
  • Using the SDEQ asm instruction.
References:

Determine if the cells are equal

We can determine if two cells are equal by comparing their hashes.
Reference: cell_hash() in docs

Determine if the tuples are equal

A more advanced approach involves iterating through tuples and comparing each value recursively. Since tuples can contain different data types, we must check and cast values dynamically.
References:

Generate an internal address

When deploying a new contract, we need to generate its internal address because it is initially unknown. Suppose we already have state_init, which contains the code and data of the new contract. This function creates an internal address corresponding to the MsgAddressInt TLB.
Note: In this example, we use workchain() to retrieve the WorkChain ID. You can learn more about the WorkChain ID in docs. Reference: cell_hash() in docs

Generate an external address

We use the TL-B scheme from block.tlb to determine the address format to generate an external address.
Since we need to find the exact number of bits occupied by the address, we must 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 in docs

How to store and load dictionary in a local storage

The logic for loading a dictionary from local storage is as follows:
Storing the dictionary follows a similar approach, ensuring data persistence.
References:

How to send a simple message

To send a simple message with a comment, prepend the message body with 32 bits set to 0, indicating that it is a comment.
Reference: Message layout in docs

How to send a message with an incoming account

A proxy contract can facilitate secure message exchange if interaction between a user and the main contract is needed.
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.
References:

How to send a message with a long text comment

A cell can store up to 127 characters (<1023 bits). A sequence of linked cells (“snake cells”) must be used if more space is required.
Reference: Internal messages in docs

How to get only data bits from a slice (without refs)

If refs within a slice are unnecessary, only the raw data bits can be extracted for further processing.
References:

How to define a custom modifying method

Modifying methods allow data to be updated within the same variable, similar to references in other programming languages.
Reference: Modifying methods in docs

How to raise number to the power of n

How to convert string to int

How to convert int to string

How to iterate dictionaries

Dictionaries are useful for managing large datasets. The built-in methods dict_get_min? and dict_get_max retrieve the minimum and maximum key values, while dict_get_next? allows dictionary iteration.
References:

How to delete value from dictionaries

How to iterate a cell tree recursively

Each cell can store up to 1023 bits of data and 4 refs. A tree of cells can be used to handle more complex data structures, requiring recursive iteration.
References:

How to iterate through lisp-style list

A tuple can hold up to 255 values. If more space is needed, a lisp-style list can be used by nesting tuples within tuples, effectively bypassing the limit.
References:

How to send a deploy message (with StateInit only, with StateInit and body)

How to build a StateInit cell

How to calculate a contract address (using StateInit)

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, you may realize that the functionality for decrementing the counter is missing. To fix this, copy the code from CounterV1 and add a new decrease function next to the existing increase function. Your updated code will look like this:
Once the CounterV2 smart contract is ready, you need to compile it off-chain into a cell and send an upgrade message to the CounterV1 contract:
References: