DEV Community

Cover image for A Comprehensive Guide to Implicit Fields in TL-B
Salikh Osmanov
Salikh Osmanov

Posted on

A Comprehensive Guide to Implicit Fields in TL-B

Introduction

In this article, we’ll dive deep into the concept of implicit fields in the TL-B language — a critical feature for understanding TON blockchain data structures.

When developing smart contracts on TON, TL-B schemas are used to define standardized cell layouts such as CommonMsgInfo, StateInit, and many others. Among all aspects of TL-B, implicit fields and their value-assignment rules are some of the trickiest to master.

Understanding implicit fields is essential for:

  • Correctly interpreting .tlb schemas
  • Implementing serialization and deserialization logic
  • Writing tools and parsers for TON data

In this article, we’ll explore:

  • What implicit fields are and how they are defined
  • Where and why they are used
  • The two main usage scenarios (parameterized types and derived values)
  • Constraints and the rules for constraint expressions
  • Best practices and naming conventions

Before you continue, it’s worth reviewing the official TL-B documentation:

Most examples in this article are drawn from the official TON TL-B schemas and documentation:

  • block.tlb — defines the structure of blocks and block headers
  • hashmap.tlb — defines hashmaps and their serialization in TL-B
  • boc.tlb — defines the Bag of Cells (BOC) container format

For deeper understanding of how these schemas are used in TON layouts, consult the official documentation:

Note: I’ll use TypeScript-style pseudocode to illustrate TL-B objects.


Understanding Implicit Fields in TL-B

According to the official TL-B documentation:

“Some fields may be implicit. These fields are defined within curly braces { }, indicating that they are not directly serialized. Instead, their values must be deduced from other data, usually the parameters of the type being serialized.”

Before diving deeper, let’s clarify a term used throughout this article — the resulting bitstring (or simply bitstring).

This is the binary string produced after serializing an object according to its TL-B definition. It represents the actual bits written into a cell and is used to reconstruct the object during deserialization.

For example:

const A = {
  x: 2,
  y: 3
};
Enter fullscreen mode Exit fullscreen mode

and the corresponding TL-B combinator declaration:

constructor1$10 x:(## 4) y:(## 6) = A;
Enter fullscreen mode Exit fullscreen mode

Serialization proceeds as follows:

  1. The first two bits "10" represent the constructor tag.
  2. Then we encode x = 2 using 4 bits: 0010.
  3. Then we encode y = 3 using 6 bits: 000011.

The resulting bitstring is:

"10" + "0010" + "000011"100010000011


What Are Implicit Fields?

TL-B has two kinds of fields:

  • Explicit fields — values are represented in the resulting bitstring.
  • Implicit fields — values are not represented in the bitstring but are derived or passed as parameters.

Implicit fields make TL-B schemas more compact and expressive. They let you describe structural relationships and parameter dependencies without wasting bits on redundant data.


Defining Implicit Fields

Curly braces { } in TL-B can serve several purposes:

  1. Defining implicit fields
  2. Expressing constraints on values
  3. Declaring computed or returned fields

The general syntax for an implicit field is:

{ field_name : field_type }
Enter fullscreen mode Exit fullscreen mode

Allowed Field Types

  • Natural Numbers (#)
  { x:# }
Enter fullscreen mode Exit fullscreen mode

Here, # represents a natural number (usually a 32-bit unsigned integer).

  • Type Parameters (Type)
  { X:Type }
Enter fullscreen mode Exit fullscreen mode

Naming Conventions

Field Type Naming Convention Example
Natural number lowercase {n:#}
Type parameter uppercase {X:Type}

Constraints in TL-B

In TL-B, constraints are expressions enclosed in { } that specify conditions a value must satisfy.

They are not serialized — instead, they act as logical rules that implementations must enforce during serialization or deserialization.

Constraints are widely used in TON schemas to ensure consistency between related fields.

1. Boolean constraints

A simple comparison or logical assertion:

flags:(## 8) { flags <= 1 }
Enter fullscreen mode Exit fullscreen mode

This means that the 8-bit field flags must not exceed 1.

Such constraints are checked during serialization or parsing, ensuring schema consistency.

2. Relational constraints

Constraints can express relationships between multiple fields:

seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr }
Enter fullscreen mode Exit fullscreen mode

Here, vert_seq_no must be greater than or equal to vert_seqno_incr.

No extra bits are stored — this condition just governs valid combinations of values.

3. Derived (computed) constraints

Constraints can also define the value of an implicit field, indicated by a tilde (~) operator:

{ prev_seq_no:# } { ~prev_seq_no + 1 = seq_no }
Enter fullscreen mode Exit fullscreen mode

This means that prev_seq_no is implicitly defined by

prev_seq_no = seq_no − 1

(expressed as ~prev_seq_no + 1 = seq_no, since is not allowed in TL-B).

It’s not read from the bitstring — instead, it’s derived from another value.

You can also use multiplication (*) to express relationships that would otherwise use division.

Since / is not permitted in TL-B, expressions such as “x = y / 2” must be rewritten using multiplication:

{ ~x * 2 = y }
Enter fullscreen mode Exit fullscreen mode

This constraint means x = y / 2, but expressed through the only allowed operation *.

This pattern appears in TL-B when the number of elements, bits, or cells must be a multiple or divisor of another count.

If the tilde (~) operator still confuses you, I’ve written a detailed explanation of it and its usage in TL-B here: 👉 Negate Operator (~) in TL-B Explained

4. Fixed-value constraints

Sometimes, a constraint simply fixes a constant:

{ ~len = 8 }
Enter fullscreen mode Exit fullscreen mode

This is equivalent to declaring that the implicit field len always equals 8.

Summary of Constraint Usage

Type Purpose Example
Boolean Limit a value { flags <= 1 }
Relational Relate two fields { vert_seq_no >= vert_seqno_incr }
Derived Define one value from another { ~prev_seq_no + 1 = seq_no }
Fixed Hard-coded constant { ~len = 8 }

Constraints are one of the most elegant features of TL-B — they make schemas self-validating, documenting both data layout and expected relationships.


Placement of Implicit Field Definitions

By convention, implicit field definitions — that is, parts like { prev_seq_no:# } which declare an implicit variable — are usually placed at the beginning of a combinator declaration.

These definitions introduce implicit parameters or values that may later be used by other fields or constraints in the same combinator.

However, this placement is a convention, not a strict requirement — they can appear anywhere within the definition as long as references remain valid.

In contrast, constraints (for example, { ~prev_seq_no + 1 = seq_no }) typically follow field definitions, since they describe logical relationships among already declared fields.

Example excerpt from block.tlb:

block_info#9bc7a987 version:uint32
  not_master:(## 1)
  after_merge:(## 1) before_split:(## 1)
  after_split:(## 1)
  want_split:Bool want_merge:Bool
  key_block:Bool vert_seqno_incr:(## 1)
  flags:(## 8) { flags <= 1 }
  seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr }
  { prev_seq_no:# } { ~prev_seq_no + 1 = seq_no }
  shard:ShardIdent gen_utime:uint32
  start_lt:uint64 end_lt:uint64
  gen_validator_list_hash_short:uint32
  gen_catchain_seqno:uint32
  min_ref_mc_seqno:uint32
  prev_key_block_seqno:uint32
  gen_software:flags.0?GlobalVersion
  master_ref:not_master?^BlkMasterInfo
  prev_ref:^(BlkPrevInfo after_merge)
  prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0)
  = BlockInfo;
Enter fullscreen mode Exit fullscreen mode

Here, { prev_seq_no:# } and { ~prev_seq_no + 1 = seq_no } demonstrate implicit definition combined with a constraint.


Obtaining Values of Implicit Fields

Suppose we have:

_ {length:#} a:(## length) ...
Enter fullscreen mode Exit fullscreen mode

Where does length come from?

Implicit fields get their values in two main ways:

  1. Through type parameters — values are passed when instantiating a type.
  2. Through constraints or derivation — values are computed or logically determined from other fields.

Example 1: Parameterized Implicit Field

Incorrect definition:

_ val:(## length) = IntVal length; // invalid
Enter fullscreen mode Exit fullscreen mode

Here, length is not declared, so this schema is invalid.

Correct version:

_ {length:#} val:(## length) = IntVal length;
Enter fullscreen mode Exit fullscreen mode

Now length is an implicit parameter — it’s passed in rather than serialized.

Example 2: Derived (Calculated) Implicit Field

We can define implicit fields whose values are derived from others using the tilde (~) operator.

For example:

_ {length:#} val:(## length) { ~length = 8 } = Int8BitVal;
Enter fullscreen mode Exit fullscreen mode

Here { ~length = 8 } acts as a constraint, meaning “length must equal 8.”

Implementations can interpret this as a computed or fixed value.

Example 3: Returned Implicit Value

Sometimes a field’s value is returned by another type using the ~ notation:

_ {length:#} val:(## length) = IntVal length ~val;
Enter fullscreen mode Exit fullscreen mode

This means that IntVal returns its val, making it accessible to the enclosing type.

We can then use this returned value:

_ {v:#} a:(IntVal 8 ~v) b:(## v) = A;
Enter fullscreen mode Exit fullscreen mode

Here, v is an implicit field that obtains its value from IntVal 8 ~v, and that value determines the bit length of b.


Serialization and Deserialization

It’s important to distinguish between parameterized and derived implicit fields.

  • Parameterized fields must be known before serialization or deserialization, since their values define bit sizes or type choices.
  • Derived fields are computed during deserialization — they are not read from the bitstring but inferred from other data.

Example: Parameterized Field

_ {x:#} my_val:(## x) = A x;
Enter fullscreen mode Exit fullscreen mode

If we serialize:

const A = { my_val: 5, x: 6 };
Enter fullscreen mode Exit fullscreen mode

We must know x = 6 before serialization to encode the correct number of bits.

During deserialization, we also need x to know how many bits to read.

Resulting bitstring: 6 bits representing the binary form of 5 → 000101.

Example: Derived Field

_ {a:#} my_val:(## x) { ~x = a + 1 } = A;
Enter fullscreen mode Exit fullscreen mode

Here, x is not provided externally.

Instead, during deserialization, after reading a, we can compute x = a + 1 and use it to read my_val.

Thus, calculated implicit fields are derived, not deserialized — their values are reconstructed logically.


Summary

Case Source of Value Serialization Behavior Example
Explicit field Directly stored in bitstring Serialized a:(## 8)
Implicit (parameterized) Passed as type parameter Not serialized {n:#} value:(## n)
Implicit (derived) Computed from other fields Not serialized { ~x = a + 1 }
Constraint Restriction or relation Not serialized { flags <= 1 }
Returned value Returned by nested type Not serialized Type ~field

Conclusion

Implicit fields and constraints are key features of TL-B that make TON schemas compact yet expressive.

They allow you to parameterize structures, enforce logical relationships, and define derived values — all without consuming extra bits in serialization.

When implementing parsers or code generators for TL-B, always remember:

  • Implicit ≠ serialized — their values come from parameters or computation.
  • Constraint expressions are limited: use + and * only, and express subtraction or division through equivalent additive or multiplicative forms.
  • Constraints { ~x = expr } and { condition } define logical relationships, not procedural code.
  • Placement is flexible, though usually at the beginning for clarity.

Mastering implicit fields and constraints will make your understanding of TON’s TL-B language much deeper and your tools more robust.

Top comments (0)