Разбор метаданных
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.
Стандарт метаданных, который включает NFT, коллекции NFT и Jettons, описан в TON Enhancement Proposal 64 [TEP-64] (https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md).
В TON сущности могут иметь три типа метаданных: on-chain, semi-chain, и off-chain.
- On-chain метаданные: хранятся внутри блокчейна, включая название, атрибуты и изображение.
- Off-chain метаданные: хранятся с помощью ссылки на файл метаданных, размещенного вне блокчейна.
- Semi-chain метаданные: гибрид между этими двумя способами, который позволяет хранить небольшие поля, такие как имена или атрибуты, внутри блокчейне, в то время как изображение хранятся за пределами блокчейна и при этом только ссылка на него.
Кодирование данных Snake
Формат кодирования Snake позволяет часть данных хранить в стандартной ячейке, а оставшуюся часть - в дочерней ячейке (рекурсивно). Формат кодирования Snake должен иметь префикс в виде байта 0x00. Схема TL-B:
tail#_ {bn:#} b:(bits bn) = SnakeData ~0;
cons#_ {bn:#} {n:#} b:(bits bn) next:^(SnakeData ~n) = SnakeData ~(n + 1);
Формат Snake используется для хранения дополнительных данных в ячейке, когда данные превышают максимальный размер, который можно хранить в одной ячейке. Это достигается путем хранения части данных в корневой ячейке, а оставшиеся части - в первой дочерней ячейке, и так продолжается рекурсивно до тех пор, пока все данные не будут сохранены.
Вот пример кодирования и декодирования формата Snake в 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);
}
Следует отметить, что префикс 0x00
байт в корневой ячейке не всегда требуется при использовании формата snake, как в случае с off-chain содержимым NFT. Т акже ячейки заполняются байтами вместо битов для упрощения анализа. Чтобы избежать проблемы добавления ссылки (в пределах следующей дочерней ячейки) на ссылку после того, как она уже была записана в родительскую ячейку, snake ячейка строится в обратном порядке.
Кодирование Chunked
Формат кодирования chunked используется для хранения данных с помощью словарной структуры данных, начиная с chunk_index и заканчивая chunk. Кодировка chunked должна иметь префикс из байта 0x01
. Схема TL-B:
chunked_data#_ data:(HashMapE 32 ^(SnakeData ~0)) = ChunkedData;
Вот пример декодирования данных в формате chunked с помощью 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 string | необязательный параметр | URI, указывающий на JSON-документ с метаданными, который используется в формате "Semi-chain content layout". |
name | UTF8 string | необязательный параметр | идентифицирует asset |
description | UTF8 string | необязательный параметр | описывает актив |
image | ASCII string | необязательный параметр | URI, указывающий на ресурс с типом mime image |
image_data | binary* | необязательный параметр | либо двоичное представление изображения для on-chain размещения, либо base64 для off-chain размещения |
Атрибуты метаданных Jetton
uri
- Необязательный параметр. Используется в формате "Semi-chain content layout". Строка ASCII. URI, указывающий на JSON-документ с метаданными.Имя
- Необязательный параметр. Строка в формате UTF8. Идентифицирует asset.description
- Необязательный параметр. Строка в формате UTF8. Описывает asset.image
- Необязательный параметр. Строка ASCII. URI, указывающий на ресурс с типом mime image.image_data
- Необязательный параметр. Либо двоичное представление изображения для onchain размещения, либо base64 для offchain размещения.symbol
- Необязательный параметр. Строка в формате UTF8. Символ токена - например, "XMPL". Используется в форме "Вы получили 99 XMPL".decimals
- Необязательный параметр. Если не указано, по умолчанию используется значение 9. Строковое значение с числом от 0 до 255, кодированное в UTF8. Количество десятичных знаков, которые использует токен — например, 8, означает разделить количество токенов на 100000000 для получения его пользовательского представления.amount_style
- Необязательный параметр. Необходим для внешних приложений, чтобы они понимали формат отображения количества jetton.
- "n" - количество jetton (значение по умолчанию). Если у пользователя 100 токенов с десятичным числом 0, то отображается, что у пользователя 100 токенов
- "n-of-total" - количество jetton из общего количества выпущенных jetton. Например, totalSupply Jetton = 1000. У пользователя есть 100 jetton в jetton wallet. Например, должно отображаться в кошельке пользователя как 100 из 1000 или любым другим текстовым или графическим способом, чтобы показать конкретное значение в общем контексте.
- "%" - процент от общего количества выпускаемых jetton. Например, totalSupply Jetton = 1000. У пользователя есть 100 jetton в jetton wallet. Например, должно отображаться в кошельке пользователя как 10%.
render_type
- Необязательный параметр. Необходим для внешних приложений, чтобы они понимали к какой группе относится jetton и как его отображать.
- "currency" - отображать как валюту (значение по умолчанию).
- "game" - отображать для игр. Будет отображаться как NFT, но при этом отображать количество jetton, учитывая параметр
amount_style
Атрибут | Тип | Условие | Описание |
---|---|---|---|
uri | ASCII string | необязательный параметр | URI, указывающий на JSON-документ с метаданными, который используется в формате "Semi-chain content layout". |
name | UTF8 string | необязательный параметр | идентифицирует asset |
description | UTF8 string | необязательный параметр | описывает asset |
image | ASCII string | необязательный параметр | URI, указывающий на ресурс с типом mime image |
image_data | binary* | необязательный параметр | либо двоичное представление изображения для on-chain размещения, либо base64 для off-chain размещения |
symbol | UTF8 string | необязательный параметр | символ токена - например, "XMPL" и используется в форме "Вы получили 99 XMPL" |
decimals | UTF8 string | необязательный параметр | количество десятичных знаков, которые использует токен. Если не указано, по умолчанию используется значение 9. Строковое значение с числом от 0 до 255, например 8, означает, что количество токенов должно быть разделено на 100000000 для получения его пользовательского представления. |
amount_style | необязательный параметр | необходим внешним приложениям, чтобы они понимали формат отображения количества jetton. Определяется с помощью n, n-of-total, %. | |
render_type | необязательный параметр | Необходим для внешних приложений, чтобы они понимали в какую группу относится jetton и как его отображать. "currency" — отображаетс я как валюта (значение по умолчанию). "game" — используется для игр, которые отображаются как NFT, но также показывают количество jetton и учитывают значение параметра amount_style. |
Параметры
amount_style
:
- n — количество jetton (значение по умолчанию). Если пользователь имеет 100 токенов с 0 десятичных знаков, то отображается, что у пользователя 100 токенов.
- n-of-total — количество jetton от общего количества выпущенных jetton. Например, если totalSupply выпускаемых jetton равно 1000 и у пользователя есть 100 jetton в кошельке, то должно отображаться в кошельке пользователя как 100 из 1000 или другим текстовым или графическим способом, чтобы показать соотношение токенов пользователя к общему количеству доступных токенов.
- % — процент от общего количества выпущенных jetton. Например, если общее количество выпускаемых jetton равно 1000 и у пользователя есть 100 jetton, то процент должен быть отображен как 10% от баланса кошелька пользователя (100 ÷ 1000 = 0,1 или 10%).
Параметры
render_type
:
- currency - отображается как валюта (значение по умолчанию).
- game - используется для игр, которые отображаются как NFT, но также показывают количество jetton и учитывают значение параметра
amount_style
.
Разбор метаданных
Чтобы разобрать метаданные, сначала необходимо получить данные NFT из блокчейна. Чтобы лучше понять этот процесс, рекомендуем ознакомиться с разделом Получение данных NFT нашей документации по обработке активов в TON.
После того, как данные NFT в блокчейне получены, их необходимо разобрать. Чтобы выполнить этот процесс, необходимо определить тип содержимого NFT, прочитав первый байт, составляющий внутреннюю структуру NFT.
Off-chain
Если строка байтов метаданных начинается с 0x01
, это означает, что тип содержимого NFT находится off-chain. Оставшаяся часть содержимого NFT декодируется с помощью формата кодирования Snake как ASCII-строка. П осле того, как правильно будет отпределен NFT URL и получены данные идентификации NFT, процесс завершен. Ниже приведен пример URL, который использует разбор метаданных содержимого NFT off-chain:
https://s.getgems.io/nft/b/c/62fba50217c3fe3cbaad9e7f/95/meta.json
Содержимое URL (сверху):
{
"name": "TON Smart Challenge #2 Winners Trophy",
"description": "TON Smart Challenge #2 Winners Trophy 1 place out of 181",
"image": "https://s.getgems.io/nft/b/c/62fba50217c3fe3cbaad9e7f/images/943e994f91227c3fdbccbc6d8635bfaab256fbb4",
"content_url": "https://s.getgems.io/nft/b/c/62fba50217c3fe3cbaad9e7f/content/84f7f698b337de3bfd1bc4a8118cdfd8226bbadf",
"attributes": []
}
On-chain и Semi-chain
Если строка байтов метаданных начинается с 0x00
, это указывает на то, что NFT использует либо on-chain, либо semi-chain формат.
Метаданные для нашего NFT хранятся в словаре, где ключ - это SHA256-хэш имени атрибута, а значение - данные, хранящиеся либо в формате Snake, либо в формате Chunked.
Чтобы определить тип используемого NFT, разработчик должен прочитать известные атрибуты NFT, такие как uri
, name
, image
, description
и image_data
. Если поле uri
присутствует в метаданных, это указывает на semi-chain расположение. В таких случаях off-chain содержимое, указанное в поле uri, должно быть загружено и объединено со значениями словаря.
Пример on-chain NFT: EQBq5z4N_GeJyBdvNh4tPjMpSkA08p8vWyiAX6LNbr3aLjI0
Пример semi-chain NFT: EQB2NJFK0H5OxJTgyQbej0fy5zuicZAXk2vFZEDrqbQ_n5YW
Пример on-chain Jetton Master: EQA4pCk0yK-JCwFD4Nl5ZE4pmlg4DkK-1Ou4HAUQ6RObZNMi
Пример разбора on-chain NFT: stackblitz/ton-onchain-nft-parser
Важные замечания по метаданным NFT
- Для метаданных NFT обязательными являются поля
name
,description
иimage
(илиimage_data
), чтобы отображать NFT. - Для метаданных Jetton обязательными являются поля
name
,symbol
,decimals
иimage
(илиimage_data
). - Важно помнить, что любой может создать NFT или jetton с любым
name
,description
илиimage
. Чтобы избежать путаницы и потенциального мошенничества, пользователи всегда должны отображать свои NFT так, чтобы они четко отличались от других частей приложения. Вредоносные NFT и jetton могут быть отправлены в кошелек пользователя с вводящей в заблуждение или ложной информацией. - Некоторые элементы могут иметь поле
video
, которое ссылается на видео-содержимое, связанное с NFT или Jetton.