TON 元数据解析
元数据标准涵盖了 NFT、NFT 集合和 Jettons,在 TON 增强提案 64 TEP-64 中有所描述。
在 TON 上,实体可以有三种类型的元数据:链上、半链上和链下。
- 链上元数据: 存储在区块链内部,包括名称、属性和图像。
- 链下元数据: 使用链接存储到链外托管的元数据文件。
- 半链上元数据: 两者之间的混合体,允许在区块链上存储小字段,如名称或属性,而将图像托管在链外,并仅存储指向它的链接。
蛇形数据编码
蛇形编码格式允许部分数据存储在标准cell内,而剩余部分存储在子cell内(以递归方式)。蛇形编码格式必须使用 0x00 字节作为前缀。TL-B 方案:
tail#_ {bn:#} b:(bits bn) = SnakeData ~0;
cons#_ {bn:#} {n:#} b:(bits bn) next:^(SnakeData ~n) = SnakeData ~(n + 1);
当单个cell无法存储的数据超过最大大小时,使用 蛇形格式存储额外数据。这是通过在根cell中存储部分数据,其余部分存储在第一个子cell中,并继续递归进行,直到所有数据都被存储。
以下是 TypeScript 中 蛇形格式编码和解码的示例:
export function makeSnakeCell(data: Buffer): Cell {
const chunks = bufferToChunks(data, 127)
if (chunks.length === 0) {
return beginCell().endCell()
}
if (chunks.length === 1) {
return beginCell().storeBuffer(chunks[0]).endCell()
}
let curCell = beginCell()
for (let i = chunks.length - 1; i >= 0; i--) {
const chunk = chunks[i]
curCell.storeBuffer(chunk)
if (i - 1 >= 0) {
const nextCell = beginCell()
nextCell.storeRef(curCell)
curCell = nextCell
}
}
return curCell.endCell()
}
export function flattenSnakeCell(cell: Cell): Buffer {
let c: Cell | null = cell;
const bitResult = new BitBuilder();
while (c) {
const cs = c.beginParse();
if (cs.remainingBits === 0) {
break;
}
const data = cs.loadBits(cs.remainingBits);
bitResult.writeBits(data);
c = c.refs && c.refs[0];
}
const endBits = bitResult.build();
const reader = new BitReader(endBits);
return reader.loadBuffer(reader.remaining / 8);
}
应该注意,使用 蛇形格式时在根cell中并不总是需要 0x00
字节前缀,就像链下 NFT 内容的情况一样。此外,cell中以字节而非位填充,以简化解析。为了避免在其父cell已经写入后再向下一个子cell添加引用的问题,snake cell是以反向顺序构造的。
分块编码
分块编码格式使用字典数据结构存储数据,从 chunk_index 到 chunk。分块编码必须使用 0x01
字节作为前缀。TL-B 方案:
chunked_data#_ data:(HashMapE 32 ^(SnakeData ~0)) = ChunkedData;
以下是使用 TypeScript 解码分块数据的示例:
interface ChunkDictValue {
content: Buffer;
}
export const ChunkDictValueSerializer = {
serialize(src: ChunkDictValue, builder: Builder) {},
parse(src: Slice): ChunkDictValue {
const snake = flattenSnakeCell(src.loadRef());
return { content: snake };
},
};
export function ParseChunkDict(cell: Slice): Buffer {
const dict = cell.loadDict(
Dictionary.Keys.Uint(32),
ChunkDictValueSerializer
);
let buf = Buffer.alloc(0);
for (const [_, v] of dict) {
buf = Buffer.concat([buf, v.content]);
}
return buf;
}
NFT 元数据属性
属性 | 类型 | 要求 | 描述 |
---|---|---|---|
uri | ASCII 字符串 | 可选 | 指向由 "半链上内容布局" 使用的带有元数据的 JSON 文档的 URI。 |
name | UTF8 字符串 | 可选 | 标识资产。 |
description | UTF8 字符串 | 可选 | 描述资产。 |
image | ASCII 字符串 | 可选 | 指向带有图像 mime 类型的资源的 URI。 |
image_data | 二进制* | 可选 | 链上布局的图像的二进制表示,或链下布局的 base64。 |
Jetton 元数据属性
uri
- 可选。由 "半链上内容布局" 使用。ASCII 字符串。指向带有元数据的 JSON 文档的 URI。name
- 可选。UTF8 字符串。标识资产。description
- 可选。UTF8 字符串。描述资产。image
- 可选。ASCII 字符串。指向带有图像 mime 类型的资源的 URI。image_data
- 可选。链上布局的图像的二进制表示,或链下布局的 base64。symbol
- 可选。UTF8 字符串。代币的符号,例如 "XMPL"