Cells
Cells are a data structure that can hold up to 1023 bits of data and up to 4 references to other cells. They are read-only and immutable once created.Untyped cells
The basic typecell describes an untyped cell:
Typed cells
Cell<T> represents a cell with known internal structure. Since a single cell stores at most 1023 bits, larger data is split across multiple cells that sequentially reference each other. Circular references are not allowed.
toCell() method on struct instances produces values of type Cell<STRUCT_NAME>. However, any typed cell can be assigned to an untyped cell directly.
Slices
A cell opened for reading is called a slice. They allow loading data from cells. To read data from a cell manually, callbeginParse() to get a slice:
Prefer distinct slice types
Builders and slices are low-level primitives for constructing and parsing cells. The values ofbuilder and slice types contain raw binary data.
As such, reading an arbitrary slice or builder from another slice is impossible:
CantBeRead.fromCell(c) fires an error: “Can not be deserialized, because CantBeRead.s is slice”.
Express the shape of data using the type system and fixed-size slices to make serialization distinct. For example, use s: bits100 to hold a slice containing at most 100 bits.
Fixed-size slices
Theslice type cannot be serialized, but the bitsN types, such as bits32 or bytes8, can. At runtime, bitsN is a regular slice, just how int32 are regular TVM integers.
slice to bitsN, use the unsafe as operator. This is intentional: slices may have refs, so explicit casting forces consideration of whether the transformation is valid.
At runtime, this is a no-op:
Embed constant slices into a contract
TVM does not have a dedicated string type: they are emulated using slices.
stringHexToSlice("<HEX_BYTES>") to embed hexadecimal binary data:
Builders
A cell under construction is called a builder. They allow composing new cells. To construct a cell manually, create a builder, write data to it, then finalize:storeXYZ return self, which is a builder, so calls can be chained:
Read from a builder
The only way to access bits already written to a builder is to convert it into a slice:b.endCell().beginParse() is optimized and does not create intermediate cells.
Automatic construction and parsing
Structures are auto-serializable to and from cells when their fields are well-typed. Prefer using structures over manual work with builders and slices.Marshall to and from cells
Cell<T>, call load() to get a value of type T:
fromCell() calls beginParse() and reads data from a slice.
Marshall to and from builders and slices
A struct can be parsed not only from a cell but also from a slice:loadUint() and similar methods, there is loadAny<T>():
Notice that
T.fromSlice(s) does not mutate a slice s, while s.loadAny<T>() does. This is due to how Tolk handles mutability: all s.loadXYZ() methods mutate a slice, but functions in the form doSomething(s) do not.storeAny<T>() for a builder accepts any serializable value:
loadAny<T>() and storeAny<T>() work with arbitrary types, not only with structures:
Remaining slice when reading
A common pattern is to read a portion of data and then retrieve the remainder. With manual parsing, this happens naturally:RemainingBitsAndRefs:
obj = WithPayload.fromSlice(s) is a structure instance where obj.payload contains all bits and refs left as a slice.
A field with this type must appear last in a struct: no more data exists after reading it.
Stack layout and serialization
Structure fields ofcell and Cell<T> types which are serialized as references to corresponding TVM cells, with nullable types ? serialized as 0 when the value is null and 1, followed by the cell reference otherwise.
The primitive types builder and slice cannot be serialized. Use bitsN and RemainingBitsAndRefs types instead.