Editable NFT-контент

Обновишь metadata и royalty editable-коллекции: кто имеет право менять content и как собрать сообщение смены.

Что можно менять после минта

Базовый TEP-62 не требует editable. Расширения (Getgems / reference editable NFT, draft NFTEditable) добавляют методы смены collection content, common item prefix и часто royalty_params. Меняет только owner коллекции (или отдельный editor, если так заложено).

Смена off-chain JSON по тому же URI без on-chain tx тоже «редактирует» вид в кошельке — но индексаторы кэшируют; on-chain смена URI надёжнее фиксирует новую ссылку.


Меняем content и royalty коллекции

typescript
// @ton/core ^0.59.0, @ton/ton ^15.1.0
// Layout: TON Docs cookbook / Getgems editable collection
// op = 4 → change content + royalty (сверь с исходником своей collection)
import { Address, beginCell, toNano } from "@ton/core";

function offchainUri(uri: string) {
  return beginCell().storeUint(0x01, 8).storeStringTail(uri).endCell();
}

/**
 * Меняет:
 * 1) collection metadata URL
 * 2) common prefix для item metadata
 * 3) royalty: factor/base → royalty = price * factor / base
 *
 * Пример: factor=5, base=100 → 5% на royaltyAddress
 */
export function buildChangeContent(opts: {
  collectionMetaUrl: string;
  commonItemPrefix: string;
  royaltyFactor: number;
  royaltyBase: number;
  royaltyAddress: Address;
  queryId?: bigint;
}) {
  const collectionMeta = offchainUri(opts.collectionMetaUrl);
  const nftCommonMeta = offchainUri(opts.commonItemPrefix);

  const contentCell = beginCell()
    .storeRef(collectionMeta)
    .storeRef(nftCommonMeta)
    .endCell();

  const royaltyCell = beginCell()
    .storeUint(opts.royaltyFactor, 16)
    .storeUint(opts.royaltyBase, 16)
    .storeAddress(opts.royaltyAddress)
    .endCell();

  return beginCell()
    .storeUint(4, 32) // change_content — не универсальный opcode, сверь контракт
    .storeUint(opts.queryId ?? 0n, 64)
    .storeRef(contentCell)
    .storeRef(royaltyCell)
    .endCell();
}

// Отправка только с owner коллекции
export function changeCollectionContentMessage(collection: Address) {
  return {
    to: collection,
    value: toNano("0.05"),
    body: buildChangeContent({
      collectionMetaUrl: "https://example.com/nft/collection-v2.json",
      commonItemPrefix: "https://example.com/nft/v2/",
      royaltyFactor: 5,
      royaltyBase: 100,
      royaltyAddress: Address.parse(
        "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N",
      ),
    }),
  };
}

После tx проверь get_collection_data / royalty_params и что get_nft_content отдаёт новый полный URI.

Для item-level editable (если контракт поддерживает): отдельный opcode на item, вызываемый owner айтема или editor — не путай с owner коллекции.

Как это работает

  • Сообщение → collection, sender == owner коллекции.
  • Новый content cell заменяет ссылки; individual суффиксы айтемов могут остаться прежними (0.json).
  • Royalty cell обновляет TEP-66 параметры для будущих sale.
  • Кошельки подтянут новый JSON после рефетча; кэш маркетплейса может отставать минуты–часы.

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

Шлёшь change_content не с owner → коллекция отклоняет; URI не меняется.

Сменил JSON на CDN, но не on-chain URI → часть индексеров продолжает старый кэш по старому хэшу/URL.

Другой opcode, чем в твоём FunC/Tact → эталон 4 из cookbook не универсален. Смотри исходник.


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация