Cells
A cell is the fundamental data structure in TON. It’s a container that holds up to 1023 bits of data and up to 4 references to other cells. Everything in TON (contracts, messages, storage) is represented using cells. They are read-only and immutable once created. Read more about cells. In Tolk, the basic typecell describes “some cell”.
Typed cells: Cell<T>
Besides “some cell”, Tolk has a “cell with known shape” Cell<T>.
Since one cell can store only 1023 bits, when storage exceeds this limit, the solution is to split it
into multiple cells, so they become referencing each other.
Yes,
point.toCell() really gives cell implicitly.
Slices: cells opened for reading
To manually read data from a cell, usebeginParse() to get a slice:
Builders: cells at the moment of writing
To manually construct a cell, create a builder, write some data, and finalize this builder:storeXXX return self, these calls can be chained:
How to read from a builder
The only way to access already written bits is to convert a builder into a slice:b.endCell().beginParse() is optimized to a cheap asm instruction BTOS without intermediate cell creation.
Auto packing to/from cells
Tolk type system is designed to avoid cumbersome manual work with slices and builders. Almost every practical use case can be represented with an auto-serializable structure.Cell<T>, just call load() to get T:
fromCell() does beginParse() and reads data from a slice.
Auto packing to/from builders/slices
A struct can be parsed not only from a cell but also from a slice:loadUint() and others, there is a loadAny<T>() method:
By analogy, a call doSmth(s), does not change s, whereas s.loadAddress() shifts its internal pointer.
To check that no data is left after fromSlice, no actions are required: an option
assertEndAfterReading is true by default.
storeAny<T>() accepts any serializable value:
Builders and slices can NOT be serialized
Builders and slices are low-level primitives used for constructing and parsing cells. They contain raw binary data. For this reason, attempting to read an arbitrary slice from another slice is impossible: how many bits should be read?CantBeRead.fromCell(c) will fire an error “Can not be deserialized, because CantBeRead.s is slice”.
Express shape of data using the type system to make serialization distinct. For example, s: bits100 if it’s exactly 100 bits.
Type bitsN: fixed-size slices
By analogy: int can not be serialized, but int32 and int64 can.
The same: slice can not be serialized, but bits32 and bytes8 can.
At runtime, bitsN is a TVM SLICE, like int32 is a TVM INT.
bitsN to slice and back, use unsafe as operator. It’s intentional, because slices may have refs, so explicit casting forces a programmer to think whether this transformation is valid.
”The remaining” slice when reading
A common pattern is to read a portion of data and then retrieve the remainder. With manual parsing, it happens naturally:RemainingBitsAndRefs.
obj = WithPayload.fromSlice(s) will return an object, where payload contains “all bits/refs left”.
This is a special type:
Embedding constant slices into a contract
A string literal is represented as a slice:stringHexToSlice("...") to embed hexadecimal binary data:
Stack layout and serialization
Bothcell and Cell<T> are backed by TVM CELL. Serialized as a reference; nullable are “maybe reference”.
The primitive types builder and slice cannot be serialized. Use bitsN and RemainingBitsAndRefs.
For details, follow TVM representation and Serialization.