Работа с дополнительными валютами сообщений (ExtraCurrency)

Соберёшь CurrencyCollection с ExtraCurrency в TypeScript и поймёшь, чем extracurrency отличается от jetton.

ExtraCurrency

Помимо Toncoin сообщение и баланс аккаунта могут нести ExtraCurrencyCollection — словарь currency_id → amount. Это нативные доп. валюты конфигурации сети, не jetton-контракты: их можно хранить и переводить, но у них нет произвольного кода.

На момент написания в mainnet набор extracurrency может быть пустым; код и exit code 38 (недостаточно extra currencies) уже заложены в TVM.


Сборка value с extracurrency в TypeScript

typescript
// @ton/core ^0.60.0 · @ton/ton ^15.2.0
import {
  Address,
  beginCell,
  Dictionary,
  internal,
  storeMessage,
  external,
} from "@ton/core";

/** currency_id → amount (VarUInteger) */
function extraCurrencies(
  entries: Array<[number, bigint]>,
): Dictionary<number, bigint> {
  const dict = Dictionary.empty(
    Dictionary.Keys.Uint(32),
    Dictionary.Values.BigVarUint(5),
  );
  for (const [id, amount] of entries) {
    dict.set(id, amount);
  }
  return dict;
}

// Пример: 0.05 TON + 12345 единиц currency_id=100
const to = Address.parse("EQD__________________________________________0");
const msg = internal({
  to,
  value: {
    coins: 50_000_000n, // nanotons
    other: extraCurrencies([[100, 12345n]]),
  },
  body: beginCell().storeUint(0, 32).storeStringTail("ec-transfer").endCell(),
  bounce: true,
});

// Дальше — вложить msg в transfer кошелька / sendFile, как обычное internal.
console.log(msg.info);
func (fallback highlight)
;; FunC: в исходящем сообщении CurrencyCollection = grams + dict
;; После store_coins(grams) кладётся maybe-ссылка/словарь extracurrency.
;; Высокоуровневый Tact SendParameters.value — это Toncoin (Int);
;; произвольный extracurrency dict обычно собирают через raw message cell.

() send_with_extra(slice dest, int grams) impure inline {
    ;; упрощённый каркас: без extracurrency dict bit=0
    cell msg = begin_cell()
        .store_uint(0x18, 6) ;; ihr_disabled + bounce + status
        .store_slice(dest)
        .store_coins(grams)
        .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) ;; пустые fees/lt/at + no state + no body
        .end_cell();
    send_raw_message(msg, 1);
}

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

  • TL-B: CurrencyCollection = grams + ExtraCurrencyCollection; extracurrency — HashmapE 32 (VarUInteger 32).
  • Jetton — отдельные контракты и сообщения transfer; extracurrency — поле value на уровне протокола.
  • Недостаток extracurrency на балансе → action phase exit code 38.
  • В Tact для обычных приложений почти всегда достаточно Toncoin + jetton; extracurrency нужен, когда сеть/конфиг реально явно задаёт currency_id.

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

Путаешь extracurrency с jetton master/wallet → разные модели; баланс jetton не появится в other dict.

Кладёшь extracurrency в body вместо value → получатель не увидит нативные units в CurrencyCollection.

Тестируешь на mainnet «наугад» с несуществующим id → перевод лишних валют не создать; сначала конфиг/testnet с объявленным id.


Что дальше

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