DEV Community

Cover image for How many addresses fit into a Cell?
Salikh Osmanov
Salikh Osmanov

Posted on

How many addresses fit into a Cell?

Intro

While developing a smart contract, I needed to store several addresses in persistent storage. At first this looked trivial, but then a simple question came up: how many addresses can I actually fit into a single cell?

Since TON storage is measured in bits and cells, and both storage and message forwarding cost gas, answering this question precisely matters. To do that, we need to look at how addresses are defined and serialized at the TL-b level.

TL-b schemes

All address formats used in TON are defined in block.tlb. The relevant schemes are:

addr_none$00 = MsgAddressExt;

addr_extern$01 len:(## 9) external_address:(bits len)
= MsgAddressExt;

anycast_info$_ depth:(#<= 30) { depth >= 1 }
rewrite_pfx:(bits depth) = Anycast;

addr_std$10 anycast:(Maybe Anycast)
workchain_id:int8 address:bits256 = MsgAddressInt;

addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len) = MsgAddressInt;

_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;
Enter fullscreen mode Exit fullscreen mode

Address constructors

There are exactly four address constructors:

  • addr_none
  • addr_extern
  • addr_std
  • addr_var

Theoretical aspects: calculating address size

In this section we calculate exact bit sizes based strictly on the TL-B definitions.

addr_none

addr_none$00 = MsgAddressExt;
Enter fullscreen mode Exit fullscreen mode
  • Constructor tag $002 bits

Exact size: 2 bits

addr_extern

addr_extern$01 len:(## 9) external_address:(bits len)
Enter fullscreen mode Exit fullscreen mode

Components:

  • Constructor tag $012 bits
  • len:(## 9)9 bits
  • external_address:(bits len)len bits, where len ∈ [0, 511]

Exact size formula:

2 + 9 + len = 11 + len bits
Enter fullscreen mode Exit fullscreen mode

Minimum size: len = 011 bits

Maximum size: len = 511522 bits

addr_std

addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256
Enter fullscreen mode Exit fullscreen mode

Breakdown:

  • Constructor tag $102 bits
  • anycast:(Maybe Anycast)
    • Presence flag → 1 bit
    • If present:
      • depth:(#<=30)5 bits
      • rewrite_pfx:(bits depth)depth bits, where depth ∈ [1, 30]
  • workchain_id:int88 bits
  • address:bits256256 bits

Exact size formula:

2 + 1 + (anycast ? (5 + depth) : 0) + 8 + 256
Enter fullscreen mode Exit fullscreen mode

Without anycast (presence bit = 0):

2 + 1 + 8 + 256 = 267 bits
Enter fullscreen mode Exit fullscreen mode

With anycast, maximum depth = 30:

2 + 1 + (5 + 30) + 8 + 256 = 302 bits
Enter fullscreen mode Exit fullscreen mode

addr_var

addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
workchain_id:int32 address:(bits addr_len)
Enter fullscreen mode Exit fullscreen mode

Components:

  • Constructor tag $112 bits
  • anycast:(Maybe Anycast)
    • Presence flag → 1 bit
    • If present:
    • depth:(#<=30)5 bits
    • rewrite_pfx:(bits depth)depth bits
  • addr_len:(## 9)9 bits
  • workchain_id:int3232 bits
  • address:(bits addr_len)addr_len bits, where addr_len ∈ [0, 511]

Exact size formula:

2 + 1 + (anycast ? (5 + depth) : 0) + 9 + 32 + addr_len
Enter fullscreen mode Exit fullscreen mode

Without anycast, maximum address length:

2 + 1 + 9 + 32 + 511 = 555 bits
Enter fullscreen mode Exit fullscreen mode

With anycast, maximum depth = 30 and maximum address length:

2 + 1 + (5 + 30) + 9 + 32 + 511 = 590 bits
Enter fullscreen mode Exit fullscreen mode

Practical assumptions (what really happens on mainnet)

Anycast is not used

Since TVM version 10, anycast addresses:

  • are not allowed as message destinations,
  • are not allowed in account addresses,
  • are no longer supported by address parsing and rewrite instructions.

In practice, this means the Maybe Anycast flag is always 0, and the Anycast payload is never present.

addr_var is not used

Currently active workchains (masterchain and basechain):

  • use addr_std,
  • use 256-bit account IDs,
  • use small workchain IDs.

addr_var exists for future extensions and is not used in real contracts today.

Practical address sizes

Internal address (addr_std, real usage)

2 (tag)
+ 1 (anycast flag)
+ 8 (workchain_id)
+ 256 (account_id)
= 267 bits
Enter fullscreen mode Exit fullscreen mode

Exact size: 267 bits

This size is fixed.

External address (addr_extern, maximum)

2 (tag)
+ 9 (length)
+ 511 (payload)
= 522 bits
Enter fullscreen mode Exit fullscreen mode

Summary

  • TON defines four address constructors: addr_none, addr_extern, addr_std, and addr_var.
  • Theoretical address sizes range from 2 bits to 590 bits.
  • On mainnet today:
    • Anycast is unused.
    • addr_var is unused.
    • All internal addresses are addr_std.

As a result, the practical internal address size is fixed and exactly:

➡️ 267 bits

Knowing this exact value is essential when designing compact storage layouts, estimating gas costs, and deciding how many addresses can fit into a single cell.

Top comments (0)