Skip to main content

Documentation Index

Fetch the complete documentation index at: https://beta-docs.ton.org/llms.txt

Use this file to discover all available pages before exploring further.

Fift is a stack-based programming language with TON-specific features that can work with cells. TVM assembly is another stack-based language designed for TON that also handles cells. What is the difference between them?

Key differences

Fift is an interpreted language. With respect to TVM, its execution happens at compile-time — when the compiler builds the smart contract code as a BoC. That compiled code contains TVM assembly for execution at run-time on the TVM itself. Fift can appear in different forms:
  • Built-in words, constants, and variables:
    5 dup * // 25 at the top of the stack
    
  • TVM instruction bitcode definitions, like in Asm.fif:
    // Tuple primitives
    x{6F0} @Defop(4u) TUPLE
    x{6F00} @Defop NIL
    x{6F01} @Defop SINGLE
    x{6F02} dup @Defop PAIR @Defop CONS
    
  • Intertwined macros and TVM opcodes, like in wallet_v3_r2.fif:
    "Asm.fif" include
    <{ SETCP0 DUP IFNOTRET // Return if recv_internal
       DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods
         1 INT AND c4 PUSHCTR CTOS 32 LDU 32 LDU NIP 256 PLDU CONDSEL  // cnt or pubk
       }>
       INC 32 THROWIF    // Fail unless recv_external
       9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU 32 LDU     // signature in_msg subwallet_id valid_until msg_seqno cs
       NOW s1 s3 XCHG LEQ 35 THROWIF    // signature in_msg subwallet_id cs msg_seqno
       c4 PUSH CTOS 32 LDU 32 LDU 256 LDU ENDS    // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key
       s3 s2 XCPU EQUAL 33 THROWIFNOT    // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet
       s4 s4 XCPU EQUAL 34 THROWIFNOT    // signature in_msg stored_subwallet cs public_key stored_seqno
       s0 s4 XCHG HASHSU    // signature stored_seqno stored_subwallet cs public_key msg_hash
       s0 s5 s5 XC2PU    // public_key stored_seqno stored_subwallet cs msg_hash signature public_key
       CHKSIGNU 35 THROWIFNOT    // public_key stored_seqno stored_subwallet cs
       ACCEPT
       WHILE:<{
         DUP SREFS    // public_key stored_seqno stored_subwallet cs _51
       }>DO<{    // public_key stored_seqno stored_subwallet cs
         8 LDU LDREF s0 s2 XCHG    // public_key stored_seqno stored_subwallet cs _56 mode
         SENDRAWMSG
       }>    // public_key stored_seqno stored_subwallet cs
       ENDS SWAP INC    // public_key stored_subwallet seqno'
       NEWC 32 STU 32 STU 256 STU ENDC c4 POP
    }>c
    
The last code fragment resembles TVM assembly because most of it actually is TVM assembly — the only difference is that none of the TVM instructions will be executed immediately. Instead, their opcodes will be embedded into the resulting smart contract BoC for further TVM execution. Where Fift works at compile-time to shape the contract’s code, TVM assembly runs that code on the actual blockchain.

Smart contract usage

(Fift) Including large BoCs in contracts

Include large BoCs with toncli by:
  1. Editing project.yaml to include fift/blob.fif:
    contract:
      fift:
        - fift/blob.fif
      func:
        - func/code.fc
    
  2. Adding the BoC to fift/blob.boc
  3. Including this code in fift/blob.fif:
    <b 8 4 u, 8 4 u, "fift/blob.boc" file>B B>boc ref, b> <s @Defop LDBLOB
    
Access the blob in the contract:
cell load_blob() asm "LDBLOB";

() recv_internal() {
    send_raw_message(load_blob(), 160);
}

(TVM assembly) Converting integers to strings

Fift primitives cannot convert integers to strings at runtime because Fift operates at compile-time. For runtime conversion, use TVM assembly like in this solution from the 3rd TON Smart Challenge:
tuple digitize_number(int value)
  asm "NIL WHILE:<{ OVER }>DO<{ SWAP TEN DIVMOD s1 s2 XCHG TPUSH }> NIP";

builder store_number(builder msg, tuple t)
  asm "WHILE:<{ DUP TLEN }>DO<{ TPOP 48 ADDCONST ROT 8 STU SWAP }> DROP";

builder store_signed(builder msg, int v) inline_ref {
  if (v < 0) {
    return msg.store_uint(45, 8).store_number(digitize_number(-v));
  } elseif (v == 0) {
    return msg.store_uint(48, 8);
  } else {
    return msg.store_number(digitize_number(v));
  }
}

(TVM assembly) Efficient modulo multiplication

Compare these implementations:
;; First version
int mul_mod(int a, int b, int m) inline_ref {               ;; 1232 gas units
  (_, int r) = muldivmod(a % m, b % m, m);
  return r;
}

;; Second version
int mul_mod_better(int a, int b, int m) inline_ref {        ;; 1110 gas units
  (_, int r) = muldivmod(a, b, m);
  return r;
}

;; Third version, uses Fift to embed exact bitcode to be invoked later by the TVM
int mul_mod_best(int a, int b, int m) asm "x{A988} s,";     ;; 65 gas units
The x{A988} opcode implements an optimized division operation with built-in multiplication. This specialized instruction directly computes just the modulo remainder of the operation, skipping unnecessary computation steps. The s, suffix then handles the result storage - it takes the resulting slice from the stack’s top and efficiently writes it into the target builder. Together, this combination delivers substantial gas savings compared to conventional approaches.