Skip to main content
At runtime, there are only 257-bit signed integers. They are represented by the Tolk’s general int type. However, at the start and end of each execution, the contract’s state is deserialized and serialized, respectively. To optimize space and reduce storage costs, it is possible to encode integer values using fewer bits. Tolk provides additional integer types to accommodate (de)serialization:
NameInclusive rangeSpace takenExamples
Signed intN-2N-1 to 2N-1-1N bits, where N is between 1 and 257int32, int257, int7
Unsigned uintN0 to 2N-1N bits, where N is between 1 and 256uint16, uint256, uint119
There are also types of variable bit-width:
NameInclusive rangeSpace takenNotes
Unsigned coins0 to 2120-1Between 4 and 124 bitsThey represent nanoToncoin, where 109 nanoToncoin equals 1 Toncoin
Unsigned varuint16Same as coinsSame as coinsRarely used
Unsigned varuint320 to 2248-1Between 5 and 253 bitsRarely used
Signed varint16-2119 to 2119-1Same as coinsRarely used
Signed varint32-2247 to 2247-1Between 5 and 253 bitsRarely used
All these types are 257-bit integers at runtime. Overflows can occur at runtime, but they are more likely during serialization.For example, subtracting 300300 from a variable of type uint8 does not cause a runtime overflow. Yet, attempting to store the result back to the same variable triggers exit code 5: integer out of expected range.

Literals

All the following constants are of int type:
// Binary literal
const TEN = 0b1010;

// Hex literal
const MAX_UINT8 = 0xFF;

// Allowed values range from -2^256 to 2^256-1
const MAX_INT = 115792089237316195423570985008687907853269984665640564039457584007913129639935;

First-class types

All integer types can be nullable, combined within a union, and otherwise used in structural or multi-valued types:
struct Demo {
    f1: int32?           // nullable
    f2: int32 | uint64   // union
    pair: (int8, coins)
}

fun demo(d: Demo) {
    if (d.f1 != null) {
        d.f1    // smart cast to `int32`
    }
    d.pair.1    // `coins`
}

No floating-point numbers

The virtual machine supports only signed 257-bit integers. Floating-point numbers do not exist. Represent monetary, Toncoin values with coins:
// 1.23 Toncoin or 1,230,000,000 nanoToncoin
const MIN_BALANCE = ton("1.23")

Serialization

Serialization works as follows:
  • int — not serializable; use intN and other types.
  • intN — a fixed N-bit signed integer.
  • uintN — a fixed N-bit unsigned integer.
  • coins — an alias to varuint16.
  • varint16 — 4 bits of length followed by an 8 * length-bit number.
  • varuint16 — unsigned version of varint16.
  • varint32 — 5 bits of length followed by an 8 * length-bit number.
  • varuint32 — unsigned version of varint32.

intN describes serialization, int does not

To automatically parse binary data, the compiler must load and store integers correctly. When designing a contract schema, fields are described in terms such as “queryID is unsigned 64-bit” and “counterValue is 32-bit”. This is translates directly in Tolk:
struct IncMessage {
    queryID: uint64
    counterValue: int32
}
As a result, IncMessage can be serialized to a cell and decoded back. The general-purpose type int represents an integer with no serialization information. Consider this struct:
struct Point {
    x: int
    y: int
}
It is valid and it is possible to create a variable p of type Point. However, a call p.toCell() would produce the following error:
error: auto-serialization via toCell() is not available for type `Point`
       because field `Point.x` of type `int` can't be serialized
       because type `int` is not serializable, it doesn't define binary width
       hint: replace `int` with `int32` / `uint64` / `coins` / etc.
To make the struct serializable, replace int with a specific integer type:
struct Point {
    x: int8
    y: int8
}

Overflow occurs only at serialization

Consider the following code:
var v: uint8 = 255;
v += 1;     // 256
The variable v there would neither overflow nor be clamped at runtime. Instead, it would be equal to 256 during subsequent execution steps. There are no runtime bounds checks, and overflows of all integer types occur only during serialization, except for the general int type, which can overflow when doing arithmetic.
struct Resp {
    outValue: uint8
}

fun demo(resp: Resp) {
    // 256, no errors yet
    resp.outValue = v;

    // A runtime overflow error that is caused by serialization
    // of the struct containing an uint8 to a cell.
    resp.toCell();
}

Generic int implicitly casts to and from any intN

All arithmetic operations on intN degrade to int and all numeric literals are of type int. To prevent further errors, Tolk disallows direct assignments between intN and intM types, when N and M are not equal.
fun takeAnyInt(a: int) { /* ... */ }
fun getAnyInt(): int { return 42 }

fun f(op: int32, qid: uint64) {
    op = qid;               // error
    op = qid as int32;      // ok

    op + query_id;          // ok, int
    if (op == qid) {}       // ok, not assignment

    takeAnyInt(op);         // ok
    op = getAnyInt();       // ok

    var amount: int32 = 1000;
    var percent: uint8 = 50;
    // ok, int
    var new = amount * percent / 100;
    // ok, int auto-casts to int32
    amount = new;
}

Type coins and function ton("0.05")

Similar to int32, Tolk has a dedicated coins type representing nanoToncoin values. The coins type has special serialization rules. It’s serialized as variadic integer: small values consume fewer, large values consume more. Arithmetic with coins degrades to int, similar to intN, except for addition or subtraction operations, where the coins type is preserved. Values of type int can be cast back to coins, following the same rules as intN. There is a ton built-in function, which calculates nanoToncoin values at compile-time. It accepts only constants and literals, e.g., ton(some_variable) is invalid.
const ONE_TON = ton("1");     // `coins`, value: 1000000000

fun calcCost() {
    val cost = ton("0.05");   // `coins`, value: 50000000
    return ONE_TON + cost;
}