Introduction To Fift
Introduction To Fift
by Amin Rezaei
Aug 29, 2022
Using the Fift language is not required for programming TON smart contracts, but if you want to get acquainted with Fift, we have prepared this article for you.
What is Fift?
Fift is a stack-based general-purpose programming language designed specifically for interacting with the TON Virtual Machine and the TON Blockchain.
What is a stack-based language?
Stack-oriented programming is a programming paradigm in which parameters are passed using a stack machine model. Stack-oriented languages work with one or more stacks, each of which can have a particular purpose. Some stack-oriented languages, such as Fift, use postfix or reverse Polish notation. Any command arguments or parameters are listed before the command. Postfix notation, for example for multiply(2, 3), would be expressed as 2, 3, multiply.
Setup
To use Fift, you'll need to have the Fift compiler binaries first. You can either compile from the sources or simply download from TON Autobuilds according to your platform.

You should be able to use Fift interpreter just by typing fift in your shell. If you encounter the following error:
[ 1][t 0][2022-05-08 14:36:43.170977500][fift-main.cpp:180]     Error interpreting standard preamble file `Fift.fif`: cannot locate file `Fift.fif`
Check that correct include path is set by -I or by FIFTPATH environment variable, or disable standard preamble by -n.
You should either set the FIFTPATH env variable to fift libs directory or pass directory path by -I flag:
fift -I /path/to/fift/libs
Types
These are some basic data types used in Fift:
Stack Notation
As mentioned before, Fift uses postfix or reverse Polish notation. Fift parser processes the code line-by-line. As it reads the line, If it encounters a value, pushes it onto the stack and If it encounters a word, executes it. Words can be assumed as function/code definitions and primitives and functions are implemented as those. All words are stored in Fift's global dictionary.

Primitives are the word that are already defined by the interpreter (in the implementation), other words are defined in Fift.fif standard library. Users can define words and extend the dictionary later.

Every time the interpreter finishes interpreting the line, it prints ok to standard output.

As an example, let's see the following code:
2 3 + .
First, 2 will be pushed to the stack then 3, + primitive will be executed which will pop two values from the stack and push their sum into the stack, at last . primitive will print the integer to standard output. So the standard output after executing the code will be:
5 ok
To understand the effect of words easily, a notation is used that shows the top of the stack before and after the word's execution. It's in word-name (before — after) format and here are some examples:
Primitives and standard library words are documented in the Fift Documentation, along with their notation and a description of what they do.
List of useful words
Here is the table of some useful words that are predefined:
Creating messages with Fift
To interact with smart contracts on TON Network we mostly need to create and send messages to them. There are several ways to do that, but Fift is the main and preferred way of creating messages.

Let's say we have a contract that requires an input body in the below format:
Internal message has the following structure:
      op_code:uint32 query_id:uint64 amount:(VarUInteger 16)
      destination:MsgAddress = InternalMsgBody;
To construct this with Fift:
"Asm.fif" include
"TonUtil.fif" include
<b 24 32 u, 0 64 u, 1 Gram,
"EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX" $>smca 2drop Addr,
b>
2 boc+>B dup Bx. cr
What it does is:

  • Imports Asm.fif and TonUtil.fif libraries
  • Constructs an empty builder
  • Appends 24 as op_code (32-bit unsigned integer)
  • Appends 0 as query_id (64-bit unsigned integer)
  • Appends 1 nanoton as amount
  • Parses address EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX and stores it to builder as destination using x,y (drops z and result) by invoking Addr, word that is defined in TonUtil.fif
  • Constructs cell from the builder
  • Writes the bag of cells to Bytes
  • Prints hex representation of the result and leaves the result at top of the stack.
Here is the execution of it with fift interpreter:

What it does is:

  • Imports Asm.fif and TonUtil.fif libraries
  • Constructs an empty builder
  • Appends 24 as op_code (32-bit unsigned integer)
  • Appends 0 as query_id (64-bit unsigned integer)
  • Appends 1 nanoton as amount
  • Parses address EQA3azUJcnicaZYflOBy_
rQhhwg79_bOy9mhXt5priuc1EuX and stores it to builder as destination using x,y (drops z and result) by invoking Addr, word that is defined in TonUtil.fif
  • Constructs cell from the builder
  • Writes the bag of cells to Bytes
  • Prints hex representation of the result and leaves the result at top of the stack.
Here is the execution of it with fift interpreter:

"Asm.fif" include
 ok
"TonUtil.fif" include
 ok
<b 24 32 u, 0 64 u, 1 Gram,
 ok
"EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX" $>smca 2drop Addr,
 ok
b>
 ok
2 boc+>B dup Bx. cr
B5EE9C7241010101003100005D0000001800000000000000001018006ED66A12E4F138D32C3F29C0E5FD68430E1077EFED9D97B342BDBCD35C5739A9EF4BC35F
 ok
.s
BYTES:B5EE9C7241010101003100005D0000001800000000000000001018006ED66A12E4F138D32C3F29C0E5FD68430E1077EFED9D97B342BDBCD35C5739A9EF4BC35F
 ok
TonWeb: An Alternative Way
TonWeb is a JavaScript SDK for The Open Network. It comes with classes that ease interacting with TON ecosystem. One of those is Cell class which can be used to create the bag of cells.

For example, the above example could be done in JS this way:
const TonWeb = require("tonweb");

const Cell = TonWeb.boc.Cell;
const Address = TonWeb.utils.Address;

let cell = new Cell();
cell.bits.writeUint(24, 32); // op_code
cell.bits.writeUint(0, 64); // query_id
cell.bits.writeCoins(1); // amount
let address = new Address("EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX"); // destination
cell.bits.writeAddress(address);
console.log(cell.print()); // print cell data like Fift
let bocBytes = cell.toBoc(false);
bocBytes.then((res)=>{
      console.log(Buffer.from(res).toString("hex"))
});
If we execute the script above, we get:
$ node message.js
x{0000001800000000000000001018006ED66A12E4F138D32C3F29C0E5FD68430E1077EFED9D97B342BDBCD35C5739A9_}

b5ee9c7241010101003100005d0000001800000000000000001018006ed66a12e4f138d32c3f29c0e5fd68430e1077efed9d97b342bdbcd35c5739a9ef4bc35f
Creating Full Messages
What we discussed in two previous sections were just only message bodies, If we want to create complete messages, we should pick the appropriate type of the message and construct it according to the TL-B Schema. Messages have two types:
  • Internal Messages: messages that contracts send to each other on the blockchain
  • External Messages: messages that come from outside the blockchain (Messages from nowhere or Inbound External Messages) or go outside the blockchain (Messages to nowhere or Outbound External Messages)
Here is the TL-B Schema for Message:

message$_ {X:Type} info:CommonMsgInfo
  init:(Maybe (Either StateInit ^StateInit))
  body:(Either X ^X) = Message X;

int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
  src:MsgAddressInt dest:MsgAddressInt 
  value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
  created_lt:uint64 created_at:uint32 = CommonMsgInfo;
ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt 
  import_fee:Grams = CommonMsgInfo;
ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt
  created_lt:uint64 created_at:uint32 = CommonMsgInfo;
To construct a full internal message we will have:
"Asm.fif" include
"TonUtil.fif" include

// message body:
<b 24 32 u, 0 64 u, 1 Gram,
"EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX" $>smca 2drop Addr,
b> constant msg_body // we define the message body and store it in a constant named msg_body, so we can later use it

<b 
 // First we define CommonMsgInfo, we're gonna pick int_msg_info$0 so first bit will be => '0'
 // as schema follows, want to disable ihr, allow bounces and is not bounced itself so we'll have => '011'
 // next is source address which we'll use addr_none so => '00' (this will be overwritten when sent to correct address)
 // concating all we'll get final bitstring => '011000' that we can append to builder
 // Note: one can use 0x18 and append it as an 6-bit uint which will result in same outcome
b{011000} s,
// Now we parse and append the destination address
"EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX" $>smca 2drop Addr,
// Appending the amount
   1 Gram,
   // We now have extra_currencies which we'll have no, so a null dictionary will be needed => '0'
   // ihr_fee and fwd_fee are zero which we'll encode as `VarUInteger 16`, it will be overwritten later
   // created_lt and create_at fields will be zero so it could be overwritten later, they're 64-bit and 32-bit
   // Now we go through message body:
   // we append a zero bit to indicate there is no init field => '0'
   // later we append another zero bit which indicates in-place serialization of the body
   // All of these values are zeros so we sum the length (let's say its m) and write a m-bit 0 uint to builder
   0 1 4 4 64 32 1 1 + + + + + + u,
   // Now we parse msg_body cell to slice and append it to builder
   msg_body <s s,
b>
// to be accessible by other contracts
dup constant internal_msg
2 boc+>B dup Bx. cr
With TonWeb:
const TonWeb = require("tonweb");

const Cell = TonWeb.boc.Cell;
const Address = TonWeb.utils.Address;

// message body
const body = new Cell();
body.bits.writeUint(24, 32); // op_code
body.bits.writeUint(0, 64); // query_id
body.bits.writeCoins(1); // amount
let address = new Address("EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX"); // destination
body.bits.writeAddress(address);

// message itself:
const message = new Cell();
message.bits.writeUint(0x18, 6); // 0x18 = 0b011000
message.bits.writeAddress(address);
message.bits.writeCoins(1);
message.bits.writeUint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1);
message.writeCell(body);


const bocBytes = message.toBoc(false);
bocBytes.then((res)=>{
      console.log(Buffer.from(res).toString("hex"))
});
The result will be:
b5ee9c724101010100620000bf62001bb59a84b93c4e34cb0fca70397f5a10c3841dfbfb6765ecd0af6f34d715ce6a0808000000000000000000000000000000001800000000000000001018006ed66a12e4f138d32c3f29c0e5fd68430e1077efed9d97b342bdbcd35c5739a97d492a80
The message we just created is an internal message and only can be sent by the contracts. If we want to send messages from outside to the blockchain, we should create an inbound external message and send it to the blockchain.

For example, we can create an external message containing an internal message to our wallet's smart contract, so the smart contract will read our internal message and send it to the other contract.


Here's an example of an inbound external message which contains the internal message we just created as the body assuming the recipient contract will read the body and send it as a full raw message (with send_raw_message):
"Asm.fif" include
"TonUtil.fif" include

// this will import the internal message that we just created 
// (it will be as internal_msg constant)
"internal.fif" include

<b 
 // Again we'll define CommonMsgInfo, we're gonna pick ext_in_msg_info$10 this time, so first two bits will be => '10'
 // next is source address which we'll use addr_none so => '00' (from nowhere)
// now we need to append destination which should be MsgAddressInt, we'll use:
// addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256  = MsgAddressInt;
// and it will not be anycast so we'll get => '100'
// after concating all of them: '1000100'
b{1000100} s,
// Now we parse and append the destination address (workchain_id:int8 address:bits256)
"EQBoOEfPe5LUnrXTPuUFIK9i4kMsltc63k3vFGkmbPpXcGo5" $>smca 2drop Addr,
// Then append import_fee
   0 Gram,
   // Now we go through message body:
   // we append a zero bit to indicate there is no init field => '0'
   // later we append a 1 bit which indicates body is a cell ref (otherwise the whoule message won't fit in 1023-bits) => '1'
   b{01} s, 
   // Now we append internal_msg as a cell reference
   internal_msg ref,
b>
2 boc+>B dup Bx. cr
with TonWeb:
const TonWeb = require("tonweb");

const Cell = TonWeb.boc.Cell;
const Address = TonWeb.utils.Address;

// message body
const body = new Cell();
body.bits.writeUint(24, 32); // op_code
body.bits.writeUint(0, 64); // query_id
body.bits.writeCoins(1); // amount
let address = new Address("EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX"); // destination
body.bits.writeAddress(address);

// message itself:
const message = new Cell();
message.bits.writeUint(0x18, 6); // 0x18 = 0b011000
message.bits.writeAddress(address);
message.bits.writeCoins(1);
message.bits.writeUint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1);
message.writeCell(body);

const ext = new Cell();
ext.bits.writeUint(0x44, 7); // 0x44 = 0b1000100
let naddr = new Address("EQBoOEfPe5LUnrXTPuUFIK9i4kMsltc63k3vFGkmbPpXcGo5"); // destination
ext.bits.writeAddress(naddr);
ext.bits.writeCoins(0);
ext.bits.writeUint(1, 2); // 1 = 0b01
ext.refs.push(message);

const bocBytes = ext.toBoc(false);
bocBytes.then((res)=>{
      console.log(Buffer.from(res).toString("hex"))
});
The result will be:
b5ee9c7241010201008800014689001a0e11f3dee4b527ad74cfb941482bd8b890cb25b5ceb7937bc51a499b3e95dc010100bf62001bb59a84b93c4e34cb0fca70397f5a10c3841dfbfb6765ecd0af6f34d715ce6a0808000000000000000000000000000000001800000000000000001018006ed66a12e4f138d32c3f29c0e5fd68430e1077efed9d97b342bdbcd35c5739a9a3cca5b3
These messages were just simple demonstrations and messages in real-life apps can get quite complicated. For example, external messages should be signed and sent along with the signature so the smart contract can verify it's from the authorized sender and execute it. Also, more complex control flows can be used in Fift, like conditions, loops, and ... . As an example we could determine if a cell will be able to get serialized in-place and act accordingly automatically.

Leveraging these capabilities helps to generalize our Fift scripts. Much more complex messages and also Fift control flows will be covered in the next sections.

Amin Rezaei (@Amin0x443) is a blockchain engineer, with experience as software engineer (Android & backend) and machine learning engineering. He has been developing smart contracts and blockchain concepts, and his involvement with TON began with the TIP DEX Contest and TON Smart Challenge #1. He is currently the developer of the dBuilder framework (https://github.com/decentralized-builder/dBuilder.py), which aims to facilitate the development on TON by using Python for Smart Contract development and interface. Follow his work on GitHub: http://github.com/AminRezaei0x443
Amin Rezaei (@Amin0x443) is a blockchain engineer, with experience as software engineer (Android & backend) and machine learning engineering. He has been developing smart contracts and blockchain concepts, and his involvement with TON began with the TIP DEX Contest and TON Smart Challenge #1. He is currently the developer of the dBuilder framework (https://github.com/
decentralized-builder/dBuilder.py), which aims to facilitate the development on TON by using Python for Smart Contract development and interface. Follow his work on GitHub: http://github.com/
AminRezaei0x443