
Getting Started with Fift
Understanding Fift
Fift is a general-purpose, stack-based programming language specifically crafted for interaction with the TON Virtual Machine and the TON Blockchain.
What Does It Mean to Be Stack-Based?
In stack-oriented programming, data is handled using a stack machine model, a paradigm where the language operates with one or more stacks that serve specific functions. Fift, like other stack-based languages, often employs postfix or reverse Polish notation. This means that command arguments or parameters are placed before the command itself. For example, instead of writing multiply(2, 3) as you would in traditional notation, you would write 2 3 multiply in postfix notation.
Installation
To get started with Fift, you’ll need to obtain the Fift compiler binaries. You can either compile them from the source code or download the precompiled binaries from TON Autobuilds, depending on your platform.
Once installed, you should be able to run the Fift interpreter by simply typing fift in your terminal. If you encounter an error message like the one below:
- “Check that the correct include path is set by -I or by the FIFTPATH environment variable, or disable the standard preamble by using -n.”
- To resolve this, you can either set the FIFTPATH environment variable to point to the Fift libraries directory, or specify the directory path using the -I flag when running the interpreter.
Stack Notation
As previously mentioned, Fift utilizes postfix or reverse Polish notation. The Fift parser processes code sequentially, line by line. When it encounters a value, it pushes that value onto the stack; when it comes across a word, it executes it. In Fift, “words” can be thought of as function or code definitions, with both primitives and functions implemented as such. All words are stored in Fift’s global dictionary.
Primitives are predefined words within the interpreter’s implementation, while other words are defined in the Fift.fif standard library. Users can also define their own words and expand the dictionary as needed.
Each time the interpreter completes processing a line of code, it outputs “ok” to indicate successful execution.
Consider the following code snippet:
- 2 3 + .
Here’s how it works: First, the number 2 is pushed onto the stack, followed by the number 3. The + primitive is then executed, which pops these two values from the stack, adds them together, and pushes the result back onto the stack. Finally, the . primitive is used to print the top value of the stack to the standard output. After running this code, the output will be:
- 5 ok
To better understand the effect of each word in Fift, a notation is used that describes the state of the stack before and after the execution of a word. This notation follows the format word-name (before — after).
For further details, including documentation on primitives and standard library words, you can refer to the Fift Documentation, which provides notations and explanations of each function.
List of Useful Words in Fift
Below is a table of some predefined words in Fift, along with their notations and descriptions:
- +
Notation: (x y — x + y)
Description: Replaces two integers, x and y, at the top of the stack with their sum, x + y. - –
Notation: (x y — x – y)
Description: Computes the difference, x – y, between two integers x and y. - *
Notation: (x y — xy)
Description: Computes the product, xy, of two integers x and y. - /
Notation: (x y — q := floor(x/y))
Description: Computes the floor-rounded quotient of two integers, x and y. - <
Notation: (x y — ?)
Description: Checks whether x < y and pushes -1 if true, or 0 otherwise. - >, =, <>, <=, >=
Notation: (x y — ?)
Description: Compares x and y, pushing -1 or 0 based on the comparison result. - “<string>”
Notation: ( — S)
Description: Pushes a string literal onto the stack. - .”<string>”
Notation: ( — )
Description: Prints a constant string to the standard output. - type
Notation: (S — )
Description: Prints a string S taken from the top of the stack to the standard output. - cr
Notation: ( — )
Description: Outputs a carriage return (newline) to the standard output. - b{<binary-data>}
Notation: ( – s)
Description: Creates a slice s containing up to 1023 data bits, specified in <binary-data>, which is a string of ‘0’ and ‘1’ characters. - x{<hex-data>}
Notation: ( – s)
Description: Creates a slice s containing up to 1023 data bits, specified in <hex-data>. - <b
Notation: ( – b)
Description: Creates a new, empty builder b. - b>
Notation: (b – c)
Description: Converts a builder b into a new cell c containing the same data as b. - i,
Notation: (b x y – b’)
Description: Appends the binary representation of a signed y-bit integer x (where 0 <= y <= 257) to builder b. - u,
Notation: (b x y – b’)
Description: Appends the binary representation of an unsigned y-bit integer x (where 0 <= y <= 256) to builder b. - ref,
Notation: (b c – b’)
Description: Appends a reference to cell c to builder b. - s,
Notation: (b s – b’)
Description: Appends data bits and references from slice s to builder b. - $,
Notation: (b S – b’)
Description: Appends string S to builder b. - B>boc
Notation: (B – c)
Description: Deserializes a “standard” bag of cells represented by bytes B, returning the root cell c. - boc+>B
Notation: (c x – B)
Description: Creates and serializes a “standard” bag of cells with root cell c and all its descendants, where x represents flags for additional options. - B{<hex-digits>}
Notation: ( – B)
Description: Pushes a bytes literal containing data represented by an even number of hexadecimal digits. - Bx.
Notation: (B – )
Description: Prints the hexadecimal representation of a bytes value. - file>B
Notation: (S – B)
Description: Reads a binary file named in string S and returns its contents as a bytes value. - B>file
Notation: (B S – )
Description: Creates a new binary file named in string S and writes data from bytes B into the file. - smca>$
Notation: (x y z – S)
Description: Packs a standard TON smart contract address with workchain x (a signed 32-bit integer) and in-workchain address y (an unsigned 256-bit integer) into a 48-character string S, using flags z (where +1 indicates non-bounceable addresses, +2 for testnet-only addresses, and +4 for base64url instead of base64). - $>smca
Notation: (S – x y z -1 or 0)
Description: Unpacks a standard TON smart contract address from its human-readable string representation S.
Generating Messages with Fift
When interacting with smart contracts on the TON Network, it is often necessary to create and send messages to these contracts. While there are multiple methods to accomplish this, Fift is the primary and most recommended tool for crafting these messages.
Constructing an Internal Message with Fift
Suppose we have a contract that requires an input body structured as follows:
Internal message structure:
op_code:uint32
query_id:uint64
amount:(VarUInteger 16)
destination:MsgAddress = InternalMsgBody;
To build this message using Fift, we would use the following code:
"Asm.fif" include
"TonUtil.fif" include
smca 2drop Addr,
b>
2 boc+>B dup Bx. cr
What This Code Does:
- Includes Required Libraries: It imports the Asm.fif and TonUtil.fif libraries.
- Initializes an Empty Builder: It starts by creating an empty builder.
- Appends op_code: The value 24 is appended as the op_code (a 32-bit unsigned integer).
- Appends query_id: The value 0 is appended as the query_id (a 64-bit unsigned integer).
- Appends amount: The value 1 (representing 1 nanoton) is appended as the amount.
- Parses and Appends Destination Address: The address EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX is parsed and stored in the builder as the destination. The Addr, word (defined in TonUtil.fif) is used for this, dropping unnecessary values in the process.
- Constructs the Cell: A cell is created from the builder’s data.
- Converts to Bytes: The resulting cell is serialized into a bag of cells and converted to bytes.
- Prints Hex Representation: The hex representation of the bytes is printed, and the result is left on top of the stack.
Running the Code in the Fift Interpreter:
When executed, the code would interact as follows with the 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
In this example, the final output is the hex representation of the constructed message, indicating successful execution of the code.
TonWeb: A Different Approach
TonWeb is a JavaScript SDK designed for The Open Network (TON). It provides a set of classes that simplify interaction with the TON ecosystem. Among these is the Cell class, which can be utilized to create a bag of cells.
JavaScript Implementation Example with TonWeb
The previous example can be accomplished in JavaScript using the TonWeb SDK as follows:
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 similar to Fift
let bocBytes = cell.toBoc(false);
bocBytes.then((res) => {
console.log(Buffer.from(res).toString(“hex”));
});
When you run this script, you’ll get the following output:
This script achieves the same result as the Fift example by constructing and printing the cell data, then converting it to a bag of cells in hexadecimal format.
Constructing Complete Messages
In the previous sections, we only covered how to create message bodies. However, to construct complete messages, we need to select the appropriate message type and build it according to the TL-B Schema. Messages in the TON ecosystem come in two forms:
- Internal Messages: These are messages exchanged between smart contracts within the blockchain.
- External Messages: These are messages that either originate from outside the blockchain (Inbound External Messages or “Messages from Nowhere”) or are sent out from the blockchain (Outbound External Messages or “Messages to Nowhere”).
TL-B Schema for Message Construction
To create a complete message in the TON blockchain, you must follow the TL-B Schema for message structures. Messages can be of two types:
- Internal Messages: Messages exchanged between smart contracts on the blockchain.
- External Messages: Messages coming from or going outside the blockchain, which can either be inbound (from nowhere) or outbound (to nowhere).
Here’s the TL-B Schema for constructing a 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 build this message using Fift, we would use the following code:
“TonUtil.fif” include
smca 2drop Addr,
b>
2 boc+>B dup Bx. cr
What This Code Does:
- Includes Required Libraries: It imports the Asm.fif and TonUtil.fif libraries.
- Initializes an Empty Builder: It starts by creating an empty builder.
- Appends op_code: The value 24 is appended as the op_code (a 32-bit unsigned integer).
- Appends query_id: The value 0 is appended as the query_id (a 64-bit unsigned integer).
- Appends amount: The value 1 (representing 1 nanoton) is appended as the amount.
- Parses and Appends Destination Address: The address EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX is parsed and stored in the builder as the destination. The Addr, word (defined in TonUtil.fif) is used for this, dropping unnecessary values in the process.
- Constructs the Cell: A cell is created from the builder’s data.
- Converts to Bytes: The resulting cell is serialized into a bag of cells and converted to bytes.
- Prints Hex Representation: The hex representation of the bytes is printed, and the result is left on top of the stack.
Running the Code in the Fift Interpreter:
When executed, the code would interact as follows with the 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
In this example, the final output is the hex representation of the constructed message, indicating successful execution of the code.
TonWeb: A Different Approach
TonWeb is a JavaScript SDK designed for The Open Network (TON). It provides a set of classes that simplify interaction with the TON ecosystem. Among these is the Cell class, which can be utilized to create a bag of cells.
JavaScript Implementation Example with TonWeb
The previous example can be accomplished in JavaScript using the TonWeb SDK as follows:
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 similar to Fift
let bocBytes = cell.toBoc(false);
bocBytes.then((res) => {
console.log(Buffer.from(res).toString(“hex”));
});
When you run this script, you’ll get the following output:
x{0000001800000000000000001018006ED66A12E4F138D32C3F29C
0E5FD68430E1077EFED9D97B342BDBCD35C5739A9_}
b5ee9c7241010101003100005d0000001800000000000000001018006ed66a12e4f138d32
c3f29c0e5fd68430e1077efed9d97b342bdbcd35c5739a9ef4bc35f
This script achieves the same result as the Fift example by constructing and printing the cell data, then converting it to a bag of cells in hexadecimal format.
Constructing Complete Messages
In the previous sections, we only covered how to create message bodies. However, to construct complete messages, we need to select the appropriate message type and build it according to the TL-B Schema. Messages in the TON ecosystem come in two forms:
- Internal Messages: These are messages exchanged between smart contracts within the blockchain.
- External Messages: These are messages that either originate from outside the blockchain (Inbound External Messages or “Messages from Nowhere”) or are sent out from the blockchain (Outbound External Messages or “Messages to Nowhere”).
TL-B Schema for Message Construction
To create a complete message in the TON blockchain, you must follow the TL-B Schema for message structures. Messages can be of two types:
- Internal Messages: Messages exchanged between smart contracts on the blockchain.
- External Messages: Messages coming from or going outside the blockchain, which can either be inbound (from nowhere) or outbound (to nowhere).
Here’s the TL-B Schema for constructing a message:
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;
Constructing a Full Internal Message
To construct a full internal message, follow these steps using Fift:
"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 // Define and store the message body in a constant named msg_body<b>
// Define CommonMsgInfo, starting with int_msg_info$0:
// Disable IHR, allow bounces, and mark as not bounced (binary '011')
// Use addr_none for the source address (binary '00')
// The final bitstring '011000' is appended to the builder
b{011000} s,
// Parse and append the destination address
"EQA3azUJcnicaZYflOBy_rQhhwg79_bOy9mhXt5priuc1EuX" $>smca 2drop Addr,
// Append the amount
1 Gram,
// Additional fields:
// No extra currencies, so a null dictionary is needed (binary '0')
// Zero values for ihr_fee, fwd_fee, created_lt, and created_at (overwritten later)
// Append zero bits for no init field and in-place body serialization
0 1 4 4 64 32 1 1 + + + + + + u,
// Convert msg_body cell to slice and append it to the builder
msg_body <s s b>
// Make it accessible by other contracts
dup constant internal_msg
2 boc+>B dup Bx. cr
Equivalent JavaScript Implementation with TonWeb
The same message can be constructed using TonWeb in JavaScript as follows:
const TonWeb = require("tonweb");
const Cell = TonWeb.boc.Cell;
const Address = TonWeb.utils.Address;
// Construct the 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);
// Construct the full message
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"));
});
Output
Running the above script will yield the following result:
b5ee9c724101010100620000bf62001bb59a84b93c4e34cb0fca70397f5a10c3841dfbfb6765ec
d0af6f34d715ce6a080800000000000000000000000000
The message we just generated is an internal message, meaning it can only be sent by contracts within the blockchain. If we need to send messages from outside the blockchain, we must create an inbound external message and deliver it to the blockchain.
For example, we can construct an external message that includes an internal message directed to our wallet’s smart contract. The smart contract will then process the internal message and forward it to the appropriate contract within the blockchain.
Example of Creating an Inbound External Message
Here’s an example of how to create an inbound external message that contains the internal message we previously constructed as its body. The recipient contract will read this body and send it as a full raw message using send_raw_message.
Fift Implementation:
"Asm.fif" include
"TonUtil.fif" include
// Import the internal message we previously created (stored as the internal_msg constant)
"internal.fif" include
<b
// Define CommonMsgInfo for an external inbound message using ext_in_msg_info$10:
// Set the first two bits to '10' to indicate this type of message
// Use addr_none for the source address, which is '00' (indicating from nowhere)
// For the destination, use MsgAddressInt:
// addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt;
// Without anycast, the resulting bitstring is '100'
// Concatenating these bits gives us '1000100'
b{1000100} s,
// Parse and append the destination address (workchain_id:int8 address:bits256)
"EQBoOEfPe5LUnrXTPuUFIK9i4kMsltc63k3vFGkmbPpXcGo5" $>smca 2drop Addr,
// Append the import_fee
0 Gram,
// Process the message body:
// Append a zero bit to indicate no init field => '0'
// Append a 1 bit indicating the body is a cell reference (to fit within 1023 bits) => '1'
b{01} s,
// Append internal_msg as a cell reference
internal_msg ref,
b>
2 boc+>B dup Bx. cr
JavaScript Implementation with TonWeb:
const TonWeb = require("tonweb");
const Cell = TonWeb.boc.Cell;
const Address = TonWeb.utils.Address;
// Create the internal 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);
// Create the internal 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);
// Create the external message
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"));
});
Resulting Output:
Running the above script will generate the following hex-encoded message:
b5ee9c7241010201008800014689001a0e11f3dee4b527ad74cfb941482bd8b890cb25b5ce
b7937bc51a499b3e95dc010100bf62001bb59a84b93c4e34cb0fca70397f5a10c3841dfbfb6765ecd0af6f
34d715ce6a0808000000000000000000000000000000001800000000000000001018006ed66a12
e4f138d32c3f29c0e5fd68430e1077efed9d97b342bdbcd35c5739a9a3cca5b3
These examples were basic demonstrations, but in real-world applications, messages can become much more intricate. For instance, external messages need to be signed, and the signature must be included so that the smart contract can authenticate the sender and execute the message. Additionally, more advanced control structures can be implemented in Fift, such as conditions, loops, and more. For example, we could programmatically check if a cell can be serialized in-place and adjust the process accordingly.
Utilizing these features allows us to create more versatile and reusable Fift scripts. More complex messaging structures and advanced control flows in Fift will be discussed in the upcoming sections.