Роялти при перепродаже NFT

Поймёшь TEP-66: как считаются роялти, кто их контролирует и почему маркетплейс должен сам их выплатить.

Что такое роялти на TON

TEP-66 задаёт параметры роялти на collection: доля от цены продажи и адрес получателя. Стандарт не принуждает каждый transfer платить автору — иначе нельзя подарить NFT или сменить кошелёк. Выплату делает sale/маркетплейс, который читает royalty_params и переводит долю при покупке.

Контроль: обычно owner коллекции задаёт numerator/denominator и destination при деплое или через editable-методы.


Читаем royalty_params и считаем долю

typescript
// @ton/ton ^15.1.0, @ton/core ^0.59.0
// TEP-66: royalty_params() → (numerator, denominator, destination)
import { Address, beginCell, toNano, TonClient } from "@ton/ton";

const client = new TonClient({
  endpoint: "https://toncenter.com/api/v2/jsonRPC",
});

const collection = Address.parse(
  "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c", // ← collection
);

/**
 * royalty = price * numerator / denominator
 * Пример: numerator=50, denominator=1000 → 5%
 *          numerator=5,  denominator=100  → 5%
 */
async function getRoyalty(collectionAddr: Address) {
  const res = await client.runMethod(collectionAddr, "royalty_params");
  const numerator = res.stack.readNumber();
  const denominator = res.stack.readNumber();
  const destination = res.stack.readAddress();
  return { numerator, denominator, destination };
}

function royaltyFromPrice(priceNano: bigint, numerator: number, denominator: number) {
  if (denominator === 0) return 0n;
  return (priceNano * BigInt(numerator)) / BigInt(denominator);
}

const { numerator, denominator, destination } = await getRoyalty(collection);
const price = toNano("10"); // цена продажи
const royalty = royaltyFromPrice(price, numerator, denominator);
console.log({
  percent: (numerator / denominator) * 100,
  royaltyTon: royalty.toString(),
  payTo: destination?.toString(),
});

// On-chain запрос (контракт → collection): get_royalty_params#693d3950
const OP_GET_ROYALTY_PARAMS = 0x693d3950;
const askRoyalty = beginCell()
  .storeUint(OP_GET_ROYALTY_PARAMS, 32)
  .storeUint(0n, 64) // query_id
  .endCell();
// Ответ: report_royalty_params#a8cb00ad query_id numerator denominator destination

Типичное распределение при цене 10 GRAM, royalty 5%, marketplace fee 5%:

ПолучательСуммаКто решает
Автор (royalty destination)0.5 GRAMroyalty_params на collection
Маркетплейс0.5 GRAMпараметр sale-контракта / оферты
Продавец9.0 GRAMостаток после fee и royalty

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

  • royalty_params — get-метод collection (TEP-66); destination часто кошелёк автора или DAO.
  • Формула: price * numerator / denominator в нанограммах, целочисленное деление.
  • Обычный transfer item→друг не платит роялти — только смена owner.
  • Sale при покупке сам шлёт Gram на destination; без этого поля в sale автор ничего не получит.

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

Ждёшь роялти с любого transfer → TEP-62 специально не вшивает обязательную комиссию. Только sale/маркетплейс.

Неверный denominator → 5% это не всегда 5/100; в reference часто 50/1000. Считай по on-chain значениям.

Продажа в обход маркетплейса (P2P transfer + отдельный перевод Gram) → роялти можно обойти; on-chain гарантии нет.


Что дальше

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