Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
"Visio",
"votings",
"VARUINT16",
"Wikimar",
"workchain",
"workchains",
"xguard",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Fift and TVM assembly

Fift is stack-based programming language that has TON-specific features and therefore can work with cells. TVM assembly is also a stack-based programming language, also specific to TON, and it also can work with cells. So what's the difference between them?
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's the difference between them?

## The difference
## Key differences

Fift is executed **at compile-time** - when your compiler builds smart-contract code BOC, after FunC code is processed. Fift can look differently:
Fift executes at **compile-time** - when your compiler builds the smart contract code BOC after processing FunC code. Fift can appear in different forms:

```
// tuple primitives
// Tuple primitives
x{6F0} @Defop(4u) TUPLE
x{6F00} @Defop NIL
x{6F01} @Defop SINGLE
Expand All @@ -17,60 +17,63 @@ x{6F02} dup @Defop PAIR @Defop CONS

```
"Asm.fif" include
<{ SETCP0 DUP IFNOTRET // return if recv_internal
<{ 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
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
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'
}> // 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
```
> wallet_v3_r2.fif

Last fragment of code looks like TVM assembly, and most of it really is! How can this happen?
The last code fragment resembles TVM assembly because most of it actually is TVM assembly. Here's why:

Imagine you're talking to a trainee programmer, saying him "and now add commands doing this, this and that to the end of function". Your commands end up being in trainee's program. They're processed twice - just like here, opcodes in capital letters (SETCP0, DUP, etc) are processed both by Fift and by TVM.
Imagine explaining programming concepts to a trainee. Your instructions become part of their program, processed twice - similar to how opcodes in capital letters (SETCP0, DUP, etc.) are processed by both Fift and TVM.

You can explain high-level abstractions to your trainee, eventually he will understand and be able to use them. Fift is also extensible - you're able to define your own commands. In fact, Asm[Tests].fif is all about defining TVM opcodes.
Think of Fift as a teaching language where you can introduce high-level concepts to a learner. Just as a trainee programmer gradually absorbs and applies new concepts, Fift allows you to define custom commands and abstractions. The Asm[Tests].fif file demonstrates this perfectly - it's essentially a collection of TVM opcode definitions.

TVM opcodes, on the other hand, are executed **at run-time** - they're code of smart contracts. They can be thought of as program of your trainee - TVM assembly can do less things (e.g. it doesn't have builtin primitives for signing data - because everything that TVM does in blockchain is public), but it can really interact with its environment.
TVM assembly, in contrast, is like the trainee's final working program. While it operates with fewer built-in features (it can't perform cryptographic signing, for instance), it has direct access to the blockchain environment during contract execution. Where Fift works at compile-time to shape the contract's code, TVM assembly runs that code on the actual blockchain.

## Usage in smart-contracts
## Smart contract usage

### [Fift] - Putting big BOC into contract
### [Fift] Including large BOCs in contracts

This is possible if you are using `toncli`. If you use other compilers to build contract, possibly there are other ways to include big BOC.
Edit `project.yaml` so that `fift/blob.fif` is included when building smart-contract code:
```
When using `toncli`, you can include large BOCs by:

1. Editing `project.yaml` to include `fift/blob.fif`:
```yaml
contract:
fift:
- fift/blob.fif
func:
- func/code.fc
```

Put the BOC in `fift/blob.boc`, then add the following code to `fift/blob.fif`:
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
```

Now, you're able to extract this blob from smart contract:
Now you can access the blob in your contract:
```
cell load_blob() asm "LDBLOB";

Expand All @@ -79,13 +82,10 @@ cell load_blob() asm "LDBLOB";
}
```

### [TVM assembly] - Converting integer to string
### [TVM assembly] Converting integers to strings

Fift primitives can't convert integers to strings at runtime because Fift operates at compile-time. For runtime conversion, use TVM assembly like this solution from TON Smart Challenge 3:

"Sadly", int-to-string conversion attempt using Fift primitives fails.
```
slice int_to_string(int x) asm "(.) $>s PUSHSLICE";
```
The reason is obvious: Fift is doing calculations in compile-time, where no `x` is yet available for conversion. To convert non-constant integer to string slice, you need TVM assembly. For example, this is code by one of TON Smart Challenge 3 participants':
```
tuple digitize_number(int value)
asm "NIL WHILE:<{ OVER }>DO<{ SWAP TEN DIVMOD s1 s2 XCHG TPUSH }> NIP";
Expand All @@ -95,7 +95,7 @@ builder store_number(builder msg, tuple t)

builder store_signed(builder msg, int v) inline_ref {
if (v < 0) {
return msg.store_uint(45, 8).store_number(digitize_number(- v));
return msg.store_uint(45, 8).store_number(digitize_number(-v));
} elseif (v == 0) {
return msg.store_uint(48, 8);
} else {
Expand All @@ -104,7 +104,9 @@ builder store_signed(builder msg, int v) inline_ref {
}
```

### [TVM assembly] - Cheap modulo multiplication
### [TVM assembly] Efficient modulo multiplication

Compare these implementations:

```
int mul_mod(int a, int b, int m) inline_ref { ;; 1232 gas units
Expand All @@ -118,4 +120,4 @@ int mul_mod_better(int a, int b, int m) inline_ref { ;; 1110 gas units
int mul_mod_best(int a, int b, int m) asm "x{A988} s,"; ;; 65 gas units
```

`x{A988}` is opcode formatted according to [5.2 Division](/v3/documentation/tvm/instructions#A988): division with pre-multiplication, where the only returned result is remainder modulo third argument. But opcode needs to get into smart-contract code - that's what `s,` does: it stores slice on top of stack into builder slightly below.
The `x{A988}` opcode implements an optimized division operation with built-in multiplication (as specified in [section 5.2 Division](/v3/documentation/tvm/instructions#A988)). 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.
146 changes: 100 additions & 46 deletions docs/v3/documentation/smart-contracts/fift/fift-deep-dive.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,78 @@
# Fift deep dive

A high-level stack-based language Fift is used for local manipulation with cells and other TVM primitives, mostly for converting TVM assembly code into contract code bag-of-cells.
Fift is a high-level stack-based language used for local manipulation of cells and other TVM primitives. Its primary purpose is to compile TVM assembly code into contract code as a bag-of-cells (BOC).

:::caution
This section describes interacting with TON-specific features at **very** low level.
Serious understanding of stack languages' basics required.
**Advanced topic notice**
This section covers low-level interactions with TON's implementation details. Before proceeding, ensure you have:

- Solid experience with stack-based programming paradigms
- Understanding of virtual machine architectures
- Familiarity with low-level data structures
:::

## Simple arithmetic
You are able to use Fift interpreter as calculator, writing in expressions in [reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation).

Use the Fift interpreter as a calculator with reverse Polish notation:

```
6 17 17 * * 289 + .
2023 ok
```

This example calculates:

1. `17 * 17 = 289`
2. `6 * 289 = 1734`
3. `1734 + 289 = 2023`

## Standard output

```
27 emit ."[30;1mgrey text" 27 emit ."[37m"
grey text ok
```
`emit` takes number from top of stack and prints Unicode character with the specified code into stdout.
`."..."` prints constant string.

- `emit` prints the Unicode character corresponding to the number on top of the stack
- `."..."` outputs a constant string

## Defining functions (Fift words)
The main way of defining a word is to enclose its effects in curly braces then write `:` and word name.
```

To define a word, follow these steps:

1. **Enclose the word's effects** in curly braces `{}`.
2. **Add a colon `:`** after the closing brace.
3. **Specify the word's name** after the colon.

First line defines a word `increment` that increases `x` by `1`.

**Examples:**
```
{ x 1 + } : increment
{ minmax drop } : min
{ minmax nip } : max
```
```
> Fift.fif

Though, there are several *defining words*, not only `:`. They're different in the sense words defined with some of them are **active** (work inside curly braces) and some are **prefix** (don't require space character to be after them):
```
{ bl word 1 2 ' (create) } "::" 1 (create)
{ bl word 0 2 ' (create) } :: :
{ bl word 2 2 ' (create) } :: :_
{ bl word 3 2 ' (create) } :: ::_
{ bl word 0 (create) } : create
```
In TON, multiple **defining words** exist, not just `:`. They differ in behavior:

- **Active words** – Operate inside curly braces `{}`.
- **Prefix words** – Do not require a trailing space .

```
{ bl word 1 2 ' (create) } "::" 1 (create)
{ bl word 0 2 ' (create) } :: :
{ bl word 2 2 ' (create) } :: :_
{ bl word 3 2 ' (create) } :: ::_
{ bl word 0 (create) } : create
```

> Fift.fif

## Conditional execution
Code blocks (those delimited by curly braces) can be executed, either conditionally or unconditionally.

Execute code blocks conditionally using `cond`:

```
{ { ."true " } { ."false " } cond } : ?. 4 5 = ?. 4 5 < ?.
false true ok
Expand All @@ -50,38 +81,48 @@ hello world ok
```

## Loops

Use loop primitives for repetitive operations:

```
// ( l c -- l') deletes first c elements from list l
// ( l c -- l') Removes first c elements from list l
{ ' safe-cdr swap times } : list-delete-first
```

> GetOpt.fif

Loop word `times` takes two arguments - let's call them `cont` and `n` - and executes `cont` `n` times.
Here `list-delete-first` takes continuation of `safe-cdr` (command deleting head from Lisp-style list), places it under `c` and then `c` times removes head from list present on stack.

There are also words `while` and `until`.
`while`/`until` provide conditional looping.

## Comments

Comments in Fift are defined in `Fift.fif` and come in two forms:
1. **Single-line comments**: Start with `//` and continue to the end of the line
2. **Multiline comments**: Start with `/*` and end with `*/`

```
{ 0 word drop 0 'nop } :: //
{ char " word 1 { swap { abort } if drop } } ::_ abort"
{ { bl word dup "" $= abort"comment extends after end of file" "*/" $= } until 0 'nop } :: /*
```
> Fift.fif

Comments are defined in `Fift.fif`. Single-line comment starts with `//` and continues to the end of line; multiline comment starts with `/*` and ends with `*/`.
#### How Comments Work

Fift programs are sequences of words that transform the stack or define new words. Comments must work even during word definitions, requiring them to be **active words** (defined with `::`).

Breaking down the `//` definition:
1. `0` - Pushes zero onto the stack
2. `word` - Reads characters until reaching one matching the top stack value (zero is special - skips leading spaces then reads to end of line)
3. `drop` - Removes the comment text from the stack
4. `0` - Pushes zero again (number of results for `::` definition)
5. `'nop` - Pushes an execution token that does nothing (equivalent to `{ nop }`)

Let's understand why they work so.
Fift program is essentially sequence of words, each of those transforming stack in some way or defining new words. First line of `Fift.fif` (code shown above) is declaration of new word `//`.
Comments have to work even when defining new words, so they must work in nested environment. That's why they are defined as **active** words, by means of `::`. Actions of the word being created are listed in the curly braces:
1. `0`: zero is pushed onto stack
2. `word`: this command reads chars until one equal to top of stack is reached and pushes the data read as String. Zero is special case: here `word` skips leading spaces and then reads until the end of the current input line.
3. `drop`: top element (comment data) is dropped from stack.
4. `0`: zero is pushed onto stack again - number of results, used because word is defined with `::`.
5. `'nop` pushes execution token doing nothing when called. It's pretty much equivalent to `{ nop }`.

## Using Fift for defining TVM assembly codes
```

```fift
x{00} @Defop NOP
{ 1 ' @addop does create } : @Defop
{ tuck sbitrefs @ensurebitrefs swap s, } : @addop
Expand All @@ -92,26 +133,39 @@ x{00} @Defop NOP
```
> Asm.fif (lines order reversed)

`@Defop` takes care of checking if there is enough space for opcode (`@havebitrefs`), and if there is not, it goes on writing to another builder (`@|`; also known as implicit jump). That's why you generally don't want to write `x{A988} s,` as an opcode: there could be insufficient space to place this opcode, so compilation would fail; you should write `x{A988} @addop` instead.
### How @Defop works
`@Defop` checks available space for the opcode using `@havebitrefs`. If space is insufficient, it writes to another builder via `@|` (implicit jump).

You may use Fift for including big bag-of-cells into contract:
```
**Important:** Always use `x{A988} @addop` instead of `x{A988} s,` to avoid compilation failures when space is limited.

### Including cells in contracts
You can embed large bag-of-cells into contracts:
```fift
<b 8 4 u, 8 4 u, "fift/blob.boc" file>B B>boc ref, b> <s @Defop LDBLOB
```
This command defines opcode which, when being included into program, writes `x{88}` (`PUSHREF`) and a reference to provided bag-of-cells. So when `LDBLOB` instruction is ran, it pushes the cell to TVM stack.

This defines an opcode that:
1. Writes `x{88}` (`PUSHREF`) when included in the program
2. Adds a reference to the specified bag-of-cells
3. Pushes the cell to TVM stack when executing `LDBLOB`

## Special features

- Ed25519 cryptography
- newkeypair - generates private-public key pair
- priv>pub - generates public key from private
- ed25519_sign[_uint] - generates signature given data and private key
- ed25519_chksign - checks Ed25519 signature
- Interaction with TVM
- runvmcode and similar - invokes TVM with code slice taken from stack
- Writing BOC into files:
`boc>B ".../contract.boc" B>file`
### Ed25519 cryptography
Fift provides built-in support for Ed25519 cryptographic operations:
- **`newkeypair`** - Generates a private-public key pair
- **`priv>pub`** - Derives a public key from a private key
- **`ed25519_sign[_uint]`** - Creates a signature for given data using a private key
- **`ed25519_chksign`** - Verifies an Ed25519 signature

### TVM interaction
- **`runvmcode` and similar commands** - Executes TVM with a code slice taken from the stack

## Continue learning
### File operations
- **Save BOC to file**:
```fift
boc>B ".../contract.boc" B>file
```

- [Fift: A Brief Introduction](https://docs.ton.org/fiftbase.pdf) by Nikolai Durov
## Continue learning
- [Fift: A Brief Introduction](https://docs.ton.org/fiftbase.pdf) by Nikolai Durov
Loading
Loading