Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 06.04 · 25 мин
Средний
NFTTEP-62CollectionsToken StandardsTON

NFT-коллекции на TON

NFT на TON — это не просто картинки, а целая архитектура контрактов для управления уникальными цифровыми активами. Как и Jetton, NFT на TON используют двухуровневую структуру (Collection + Item), что обеспечивает масштабируемость, но добавляет сложность по сравнению с ERC-721. Понимание NFT-стандарта необходимо для разработки маркетплейсов, игровых предметов и любых уникальных токенов.

В предыдущих уроках мы разобрали шардированную архитектуру Jetton 2.0. NFT в TON следуют тому же принципу — каждый токен существует как отдельный контракт. Стандарт TEP-62 определяет модель коллекция + элемент, где коллекция с 10,000 NFT = 10,001 отдельный контракт.


TEP-62: стандарт NFT

Collection + Item модель

В отличие от Ethereum ERC-721, где один контракт хранит все tokenId в маппинге, TON использует два типа контрактов:

NFT Collection (один контракт):

  • Metadata коллекции (имя, описание, обложка)
  • next_item_index — счётчик следующего элемента
  • owner — владелец коллекции (может минтить новые элементы)
  • deploy_nft_item() — создание нового NFT Item

NFT Item (отдельный контракт для каждого NFT):

  • index — порядковый номер в коллекции
  • collection_address — адрес коллекции-родителя
  • owner — текущий владелец (transferable — можно передать)
  • content — метаданные элемента (TEP-64)
NFT Collection → Items
NFT Collection (1 контракт)
metadata: "Cool Cats TON"
next_item_index: 10000
owner: 0xCreator...
deploy_nft_item()
NFT Item #0
(отдельный контракт)
index: 0
collection: 0xColl...
owner: 0xAlice...
content: {...}
NFT Item #1
(отдельный контракт)
index: 1
collection: 0xColl...
owner: 0xBob...
content: {...}
NFT Item #N
(отдельный контракт)
index: N
collection: 0xColl...
owner: 0xCarol...
content: {...}
NOTE

ERC-721 vs TEP-62

В Ethereum ERC-721 — это один контракт с маппингом tokenId => owner. Все 10,000 NFT живут в одном контракте. В TON каждый NFT — это отдельный контракт, развёрнутый в шардчейне владельца. Коллекция — это фабрика/индекс, а не хранилище.


Collection — фабрика, а не хранилище

Ключевой концептуальный сдвиг: NFT Collection в TON не хранит элементы. Она их создаёт (деплоит) и предоставляет индексирование.

Что делает Collection:

  1. Деплоит новые NFT Item при вызове deploy_nft_item()
  2. Вычисляет адрес любого Item по индексу: get_nft_address_by_index(index)
  3. Хранит метаданные коллекции (общие для всех элементов)
  4. Определяет royalty (процент отчислений создателю при перепродаже)

Что Collection НЕ делает:

  • Не хранит список владельцев
  • Не управляет трансферами после деплоя
  • Не знает, кому сейчас принадлежит конкретный Item
WARNING

Не думайте о Collection как о массиве NFT. После деплоя каждый NFT Item — полностью независимый контракт. Collection не может заблокировать, отозвать или изменить NFT (если это не предусмотрено в коде Item).


NFT Item: жизненный цикл

Деплой

Collection создаёт Item, передавая:

  • index — уникальный номер
  • owner — первый владелец
  • content — метаданные элемента

Адрес Item детерминирован: hash(code + init(index, collection_address)). Это позволяет вычислить адрес любого Item, зная только его индекс и адрес коллекции.

Передача (Transfer)

Передача NFT — это изменение owner в контракте Item:

User A                 NFT Item #42              User B
  |                        |                       |
  |-- transfer(User B) --->|                       |
  |                        | owner = User B        |
  |                        |-- ownership_assigned ->|
  |<-- excesses -----------|                       |
  1. Текущий владелец (User A) отправляет transfer в NFT Item
  2. Item проверяет, что отправитель — текущий owner
  3. Item обновляет owner на User B
  4. Item отправляет ownership_assigned новому владельцу
  5. Excesses возвращаются отправителю

Получение данных

Get-методы NFT Item:

МетодВозвращает
get_nft_data()init, index, collection, owner, content
get_nft_content()Полные метаданные (collection + item)

Метаданные: TEP-64 для NFT

NFT используют двухуровневые метаданные:

Collection-level metadata

{
  "name": "Cool Cats TON",
  "description": "10,000 unique cats on TON",
  "image": "https://example.com/collection-cover.png",
  "social_links": ["https://t.me/coolcats"]
}

Item-level metadata

{
  "name": "Cool Cat #42",
  "description": "A rare blue cat with laser eyes",
  "image": "https://example.com/cats/42.png",
  "attributes": [
    { "trait_type": "Background", "value": "Blue" },
    { "trait_type": "Eyes", "value": "Laser" },
    { "trait_type": "Rarity", "value": "Legendary" }
  ]
}
TIP

Item metadata обычно хранится off-chain (IPFS или HTTPS) для экономии газа. Collection предоставляет базовый URI, а Item добавляет свой suffix. Например: base_uri/42.json для Item #42.


Определение адреса NFT Item

Как кошельки и маркетплейсы находят NFT пользователя?

  1. По Collection: вызвать get_nft_address_by_index(i) для каждого i от 0 до next_item_index - 1
  2. По Item: вызвать get_nft_data() и проверить owner
  3. Через индексеры: TON API и эксплореры индексируют все NFT Item по событиям деплоя

На практике используют индексеры (TON API, Tonviewer) — перебирать все Item напрямую слишком дорого для больших коллекций.


Реализация NFT Collection в Tact

import "@stdlib/deploy";

contract NftCollection with Deployable {
    next_item_index: Int as uint32;
    owner: Address;
    collection_content: Cell;
    nft_item_code: Cell;

    init(owner: Address, collection_content: Cell, nft_item_code: Cell) {
        self.next_item_index = 0;
        self.owner = owner;
        self.collection_content = collection_content;
        self.nft_item_code = nft_item_code;
    }

    receive(msg: MintNft) {
        let ctx: Context = context();
        require(ctx.sender == self.owner, "Not owner");

        let nft_init: StateInit = self.getNftItemInit(self.next_item_index);
        send(SendParameters{
            to: contractAddress(nft_init),
            value: ton("0.05"),
            mode: 0,
            body: NftDeploy{
                index: self.next_item_index,
                owner: msg.owner,
                content: msg.content,
            }.toCell(),
            code: nft_init.code,
            data: nft_init.data,
        });

        self.next_item_index = self.next_item_index + 1;
    }

    fun getNftItemInit(index: Int): StateInit {
        return initOf NftItem(index, myAddress());
    }
}

Масштабируемость: 10,001 контрактов

Для коллекции из 10,000 NFT в TON деплоится 10,001 контракт (1 Collection + 10,000 Item). Это может показаться расточительным, но:

  • Каждый Item весит ~500 байт состояния
  • Хранение стоит ~0.0002 TON/год на Item
  • Общая стоимость хранения 10,000 NFT ~ 2 TON/год
  • Трансферы обрабатываются параллельно в разных шардах

Частые ошибки

  1. Хранят изображения и метаданные on-chain, что чрезмерно дорого; стандартный подход использует IPFS или CDN с записью URL в контракт.
  2. Не реализуют get_nft_data() в NFT Item контракте, без чего маркетплейсы и кошельки не смогут отобразить информацию о токене.
  3. Путают индекс NFT внутри коллекции и глобальный адрес NFT Item: для работы с конкретным NFT нужен его адрес, а не индекс.
  4. Забывают о royalty при реализации торговли: стандарт TON NFT поддерживает royalty, и его игнорирование нарушает ожидания создателей коллекций.

Проверка знанийKnowledge check
Почему NFT Collection в TON не может отозвать или заблокировать переданный NFT Item?
ОтветAnswer
После деплоя NFT Item -- это полностью независимый контракт со своим состоянием и логикой. Collection не имеет прямого доступа к состоянию Item (фундаментальное свойство модели акторов в TON). Если в коде Item не предусмотрен receiver для сообщений от Collection, она не может влиять на Item.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6