Комиссии при переводе jetton: кто платит газ

Разберёшь, кто платит газ при Jetton transfer: value отправителя, forward_ton, excesses и типичные суммы для UI.

Кто платит

Газ за цепочку Jetton-сообщений платит отправитель — в поле value внутреннего сообщения на свой jetton-wallet. Получатель токены получает «бесплатно» (ему не нужно подписывать), но если forward_ton > 0, часть Gram уходит ему вместе с transfer_notification.

text
Sender owner  --value (Gram)--> Sender jetton-wallet
                                    |
                                    | internal_transfer (+ gas)
                                    v
                              Receiver jetton-wallet
                                    |
                    +---------------+---------------+
                    |                               |
              excesses → response               notification → receiver owner
              (остаток Gram)                   (forward_ton в Gram)

Jetton (amount) и Gram (value / forward_ton) — разные активы. Недостаточный Gram = failed transfer при достаточном jetton-balance.


Минимальный запас в коде

typescript
// @ton/core ^0.59.0
import { Address, beginCell, toNano } from "@ton/core";

const OP_TRANSFER = 0xf8a7ea5;

/**
 * Практичные значения для простого transfer без тяжёлого forward_payload:
 * - forwardTon: 0n — без notification (P2P между людьми)
 * - forwardTon: toNano("0.01") — депозит в контракт, которому нужен notify
 * - value: toNano("0.05") … toNano("0.1") — газ на 2–3 хопа + запас
 */
function buildTransferWithFees(opts: {
  toOwner: Address;
  jettonAmount: bigint;
  responseTo: Address;
  notifyReceiver: boolean;
}) {
  const forwardTon = opts.notifyReceiver ? toNano("0.01") : 0n;
  // value должен покрыть: gas sender-wallet + gas receiver-wallet + forwardTon + запас
  const value = forwardTon + toNano("0.05");

  const body = beginCell()
    .storeUint(OP_TRANSFER, 32)
    .storeUint(0n, 64)
    .storeCoins(opts.jettonAmount)
    .storeAddress(opts.toOwner)
    .storeAddress(opts.responseTo)
    .storeBit(false)
    .storeCoins(forwardTon)
    .storeBit(false)
    .endCell();

  return { value, body, forwardTon };
}

// Пример: перевод с notification на контракт-депозит
const dest = Address.parse(
  "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N",
);
const me = dest;
const { value, body } = buildTransferWithFees({
  toOwner: dest,
  jettonAmount: 1_000_000n,
  responseTo: me,
  notifyReceiver: true,
});

console.log({
  valueTon: value.toString(),
  bodyBits: body.bits.length,
});

Точные gasUsed зависят от кода конкретного jetton и размера payload. Для UI закладывай запас; остаток вернётся на response_destination как excesses#d53276db.

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

  • value — платит sender; это единственный «счёт» за газ всей цепочки.
  • forward_ton_amount — часть value, которую receiver-wallet перешлёт owner'у с notification.
  • excesses — несгоревший Gram → response_destination (обычно отправитель).
  • Первый transfer на нового получателя дороже: может потребоваться деплой его jetton-wallet.

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

value = forward_ton без запаса на gas → compute/action phase падает, токены не доходят.

forward_ton = 0 для vault/DEX → депозит молчит: контракт не получил transfer_notification.

Ждёшь, что получатель оплатит газ → в базовом TEP-74 нет; платит тот, кто инициировал transfer.


Что дальше

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