# Fift and TVM assembly (https://docs-orhepa2tm-ton-core-docs.vercel.app/llms/languages/fift/fift-and-tvm-assembly/content.md)



<Callout type="note">
  The official smart contract language of TON Blockchain is [Tolk](/llms/tolk/overview/content.md). Fift is now a **legacy** language, with its compiler no longer maintained.
</Callout>

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 [#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:

  ```fift
  5 dup * // 25 at the top of the stack
  ```

* TVM instruction bitcode definitions, like in `Asm.fif`:

  ```fift
  // 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`:

  ```fift
  "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 [#smart-contract-usage]

### (Fift) Including large BoCs in contracts [#fift-including-large-bocs-in-contracts]

Include large BoCs with `toncli` by:

1. Editing `project.yaml` to include `fift/blob.fif`:

   ```yaml
   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`:

   ```fift
   <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:

```func
cell load_blob() asm "LDBLOB";

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

### (TVM assembly) Converting integers to strings [#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:

```func
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 [#tvm-assembly-efficient-modulo-multiplication]

Compare these implementations:

```func
;; 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](/llms/tvm/instructions/content.md) 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.
