一个 Cell 能容纳多少地址?
发布: (2026年1月17日 GMT+8 02:15)
4 min read
原文: Dev.to
Source: Dev.to
介绍
在开发智能合约时,我需要在持久化存储中保存多个地址。
由于 TON 的存储以比特和单元(cell)计量,且存储和消息转发都会消耗 gas,准确了解一个单元能容纳多少地址非常重要。为了解答这个问题,我们必须查看地址在 TL‑B 层面的定义和序列化方式。
TL‑B 地址定义
addr_none$00 = MsgAddressExt;
addr_extern$01 len:(## 9) external_address:(bits len)
= MsgAddressExt;
anycast_info$_ 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;
恰好有四种地址构造器:
addr_noneaddr_externaddr_stdaddr_var
以下章节将在严格依据 TL‑B 定义的前提下,计算每种构造器的精确比特大小。
地址构造器大小分析
addr_none
- 构造标签
$00→ 2 比特
精确大小: 2 比特。
addr_extern
- 构造标签
$01→ 2 比特 len:(## 9)→ 9 比特(长度字段)external_address:(bits len)→len比特,len ∈ [0, 511]
精确大小公式: 2 + 9 + len = 11 + len 比特
- 最小大小(len = 0):11 比特
- 最大大小(len = 511):522 比特
addr_std
- 构造标签
$10→ 2 比特 anycast:(Maybe Anycast)→ 存在标志 1 比特- 若存在:
depth:(#<=30)→ 5 比特,rewrite_pfx:(bits depth)→depth比特(depth ∈ [1, 30])
- 若存在:
workchain_id:int8→ 8 比特address:bits256→ 256 比特
精确大小公式:
2 + 1 + (anycast ? (5 + depth) : 0) + 8 + 256
- 没有 anycast(存在位 = 0):267 比特
- 有 anycast,且最大 depth = 30:302 比特
addr_var
- 构造标签
$11→ 2 比特 anycast:(Maybe Anycast)→ 存在标志 1 比特- 若存在:
depth:(#<=30)→ 5 比特,rewrite_pfx:(bits depth)→depth比特
- 若存在:
addr_len:(## 9)→ 9 比特(地址字段的长度)workchain_id:int32→ 32 比特address:(bits addr_len)→addr_len比特,addr_len ∈ [0, 511]
精确大小公式:
2 + 1 + (anycast ? (5 + depth) : 0) + 9 + 32 + addr_len
- 没有 anycast,且地址长度最大(
addr_len= 511):555 比特 - 有 anycast,最大 depth = 30 且最大地址长度:590 比特
实际考虑(TVM ≥ 10)
- 自 TVM 10 版起,anycast 地址不允许作为消息目的地,也不用于账户地址,且地址解析和 rewrite 指令也不支持。因此
Maybe Anycast标志始终为0,anycast 负载永远不会出现。 addr_var为未来扩展而定义,但 在实际合约中目前未被使用。- 当前活跃的工作链(主链和基础链)使用
addr_std,其特点是:- 256 比特的账户 ID
- 较小的工作链 ID
主网中的真实地址大小
- Anycast 未使用。
addr_var未使用。- 所有内部地址均为
addr_std。
因此 实际的内部地址大小固定为 267 比特。
了解这个精确数值对于设计紧凑的存储布局、估算 gas 成本以及决定单元中能容纳多少地址至关重要。