Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 06.03 · 30 мин
Средний
JettonTactDeployBlueprintTON

Деплой Jetton в Tact

Деплой собственного Jetton — это один из самых практичных навыков на TON, необходимый для создания токенов проекта, наградных систем и DeFi-протоколов. Практика деплоя на Tact закрепляет понимание двухуровневой архитектуры Jetton и даёт реальный код, который можно адаптировать для своих проектов.

В предыдущих уроках мы разобрали архитектуру Jetton 2.0 и поток перевода. Теперь реализуем собственный Jetton-токен на языке Tact, используя стандартную библиотеку @stdlib/jetton. Это практический урок — мы напишем код, скомпилируем и разберём каждую часть контракта.


Tact stdlib: готовые реализации

Tact предоставляет стандартную библиотеку @stdlib/jetton, которая реализует все контракты по стандарту TEP-74:

  • JettonMaster — трейт для мастер-контракта (metadata, total_supply, minting)
  • JettonWallet — трейт для контракта баланса (balance, transfer, internal_transfer)

Используя эти трейты, разработчику не нужно реализовывать логику переводов с нуля — стандартная библиотека обрабатывает все opcodes, bounce-сообщения и gas forwarding.

WARNING

Только Jetton 2.0 — никаких v1 паттернов! Jetton v1 (до августа 2025) использовал устаревшие практики оценки газа и не гарантировал размещение Jetton Wallet в шардчейне владельца. Стандартная библиотека Tact реализует Jetton 2.0 с правильным шардированием. Не копируйте код из устаревших репозиториев.


Структура контракта

Создадим файл SampleJetton.tact:

import "@stdlib/deploy";
import "@stdlib/jetton";

contract SampleJetton with Jetton, Deployable {
    totalSupply: Int as coins;
    owner: Address;
    content: Cell;
    mintable: Bool;

    init(owner: Address, content: Cell) {
        self.totalSupply = 0;
        self.owner = owner;
        self.content = content;
        self.mintable = true;
    }

    receive("Mint:100") {
        let ctx: Context = context();
        require(ctx.sender == self.owner, "Not owner");
        let msg = JettonMint{
            origin: ctx.sender,
            receiver: ctx.sender,
            amount: ton("100"),
        };
        self._mint(msg);
    }
}

Разберём каждую часть.


Импорты

import "@stdlib/deploy";
import "@stdlib/jetton";
  • @stdlib/deploy — трейт Deployable, обрабатывающий деплой контракта
  • @stdlib/jetton — трейты Jetton (мастер) и JettonWallet (кошелёк)

Трейты контракта

contract SampleJetton with Jetton, Deployable {

Jetton предоставляет:

  • Хранение totalSupply, owner, content, mintable
  • Обработку JettonMint, JettonBurnNotification сообщений
  • Автоматический деплой Jetton Wallet при первом переводе
  • Все opcodes из TEP-74

Deployable предоставляет:

  • Обработку сообщения Deploy для инициализации контракта

Состояние (state variables)

totalSupply: Int as coins;   // Общее количество выпущенных токенов
owner: Address;               // Владелец (может минтить)
content: Cell;                // Metadata по TEP-64
mintable: Bool;               // Разрешено ли создание новых токенов

as coins — сериализация как VarUInteger 16 (стандарт TON для хранения количества нанотонов/токенов).

Инициализация

init(owner: Address, content: Cell) {
    self.totalSupply = 0;
    self.owner = owner;
    self.content = content;
    self.mintable = true;
}

При деплое контракт создаётся с нулевым totalSupply. Адрес контракта детерминирован из init-данных: hash(code + init(owner, content)).

Mint receiver

receive("Mint:100") {
    let ctx: Context = context();
    require(ctx.sender == self.owner, "Not owner");
    let msg = JettonMint{
        origin: ctx.sender,
        receiver: ctx.sender,
        amount: ton("100"),
    };
    self._mint(msg);
}

Только owner может минтить токены. self._mint() — метод из трейта Jetton, который:

  1. Увеличивает totalSupply
  2. Создаёт internal_transfer в Jetton Wallet получателя
  3. Если Jetton Wallet не существует — деплоит его

TEP-64: метаданные токена

Jetton Master хранит метаданные в формате TEP-64. Для off-chain metadata:

{
  "name": "Sample Jetton",
  "symbol": "SMPL",
  "decimals": "9",
  "description": "A sample Jetton 2.0 token built with Tact",
  "image": "https://example.com/token-logo.png"
}

Этот JSON размещается на HTTPS или IPFS, а URI сохраняется в Cell контракта с префиксом 0x01.

TIP

Стандарт TON использует 9 десятичных знаков (decimals = 9) по умолчанию, аналогично нанотону. Если ваш токен представляет валюту, используйте decimals: "9". Для NFT-подобных токенов с целочисленными количествами — decimals: "0".


Компиляция и тестирование в Blueprint

В M03 мы уже работали с Blueprint — инструментарием для разработки смарт-контрактов на TON. Процесс для Jetton аналогичен:

1. Компиляция

npx blueprint build

Blueprint компилирует Tact-контракт в TVM bytecode. На выходе:

  • SampleJetton.compiled.json — скомпилированный контракт
  • SampleJetton.abi — ABI для взаимодействия

2. Тестирование в Sandbox

import { Blockchain, SandboxContract } from '@ton/sandbox';
import { toNano } from '@ton/core';
import { SampleJetton } from '../wrappers/SampleJetton';

describe('SampleJetton', () => {
    let blockchain: Blockchain;
    let jettonMaster: SandboxContract<SampleJetton>;

    beforeEach(async () => {
        blockchain = await Blockchain.create();
        const deployer = await blockchain.treasury('deployer');

        jettonMaster = blockchain.openContract(
            await SampleJetton.fromInit(
                deployer.address,
                buildOnchainMetadata({ name: "Test", symbol: "TST" })
            )
        );

        await jettonMaster.send(
            deployer.getSender(),
            { value: toNano('0.1') },
            { $$type: 'Deploy', queryId: 0n }
        );
    });

    it('should deploy and mint', async () => {
        const deployer = await blockchain.treasury('deployer');

        await jettonMaster.send(
            deployer.getSender(),
            { value: toNano('0.1') },
            "Mint:100"
        );

        const data = await jettonMaster.getGetJettonData();
        expect(data.totalSupply).toBeGreaterThan(0n);
    });
});

3. Деплой в testnet

npx blueprint run deployJetton --testnet

Blueprint подключается к testnet, запрашивает подтверждение и отправляет деплой-транзакцию. После деплоя вы получите адрес Jetton Master в testnet.

NOTE

Для практики деплоя используйте Blueprint Docker Lab из M03. Лабораторная работа предоставляет готовое окружение с настроенным Blueprint, Tact-компилятором и подключением к testnet.


Jetton Wallet: автоматический деплой

Одна из ключевых особенностей — Jetton Wallet деплоится автоматически при первом переводе. Когда вы минтите токены на адрес нового получателя:

  1. Jetton Master формирует internal_transfer с init-данными Jetton Wallet
  2. TON видит, что контракт по целевому адресу не существует
  3. TON деплоит Jetton Wallet из init-данных и затем доставляет сообщение
  4. Jetton Wallet инициализируется и увеличивает баланс

Разработчику не нужно вручную деплоить Jetton Wallet — это происходит прозрачно.


Кастомизация Jetton

Трейт Jetton можно расширить:

Ограничение минтинга

receive("Stop Minting") {
    let ctx: Context = context();
    require(ctx.sender == self.owner, "Not owner");
    self.mintable = false;
}

Передача владения

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

Кастомный минт с указанием получателя

receive(msg: MintTo) {
    let ctx: Context = context();
    require(ctx.sender == self.owner, "Not owner");
    require(self.mintable, "Minting disabled");
    let mintMsg = JettonMint{
        origin: ctx.sender,
        receiver: msg.receiver,
        amount: msg.amount,
    };
    self._mint(mintMsg);
}

message MintTo {
    receiver: Address;
    amount: Int as coins;
}

Кейс-стади: USDT на TON

Самый показательный пример продакшен-Jetton — это USD₮ на TON, выпущенный Tether в 2024 году. К Q1 2026 это крупнейший актив в TON-экосистеме за пределами самого Toncoin: оборот ~$1.28B, и распространяется он среди 150M+ пользователей TON Wallet прямо внутри Telegram. Полезно разобрать, как реальная стейблмонета упакована в стандарт TEP-74 — это объясняет, какие компромиссы делает Tact stdlib и почему стандартный шаблон достаточен для серьёзного продукта.

Архитектура: обычный Jetton с фиксированными параметрами

Внутри USD₮ — это стандартный Jetton по TEP-74 + TEP-89, без никаких специальных opcode. Контракт открыто опубликован в репозитории ton-blockchain/stablecoin-contract и использует те же концепции, что мы реализовывали выше: Master + per-owner Jetton Wallet, детерминированные адреса, internal_transfer между кошельками, provide_wallet_address для on-chain интеграций.

Параметр USD₮ЗначениеОткуда
СтандартTEP-74 + TEP-89Совместим с любым кошельком и DEX TON
Decimals6Совпадает с USD₮ на других сетях, не дефолтные 9
ЭмитентTether (admin-роль в master)Адрес в state мастера
Total supplyДинамический, обновляется при mint/burnХранится в master, ~$1.28B по Q1 2026
КонтрольAdmin может lock/unlock/force-transfer/burnРегуляторное требование Tether

Ключевое архитектурное отличие от “учебного” Jetton — это admin-функции мастера: Tether как эмитент должен уметь блокировать кошельки санкционных лиц и насильно сжигать токены при потере приватных ключей легитимного держателя. Это делается через дополнительные opcode в master, которые в обычном Jetton отсутствуют. Сам перевод между обычными пользователями работает по стандартному пути TEP-74 — никакой transfer_hook, никакого whitelisting.

NOTE

Centralized stablecoin — не “anti-crypto”

Регуляторика заставляет любого фиатно-обеспеченного эмитента иметь возможность блокировать средства (FATF требования, OFAC). USD₮ на TON в этом смысле ничем не отличается от USDC на Ethereum или USDT на Tron. Open-source контракт прозрачно показывает, что может делать admin — это лучше, чем непрозрачный enclave. Для приложений это значит: проектируйте интеграцию так, чтобы пользовательский баланс мог быть заморожен извне, и предусматривайте корректный UX для такого случая.

Газовая оптимизация: почему USD₮ дешевле

Tether вместе с core-командой TON оптимизировал контракт настолько, что перевод между двумя существующими держателями USD₮ стоит фиксированно ~0.0145 TON — примерно в 3 раза дешевле обычного Jetton. Источник экономии:

  1. Точная оценка газа. Контракт выверен под актуальные TVM-цены (Jetton 2.0 паттерн), не оставляет лишнего forward на ответных сообщениях.
  2. Минимальный код Wallet. Wallet-контракт сжат до необходимого минимума, что снижает stateInit-стоимость при первом mint.
  3. Шардирование. Wallet-контракт деплоится в шард владельца (ставится правильный workchain/префикс), что устраняет cross-shard transfer на самом частом пути.

Эта оптимизация — эмпирический ориентир: если ваш самописный Jetton требует >0.05 TON на простой transfer, значит вы либо не на Jetton 2.0, либо переплачиваете forward. Сравните с USD₮ как с baseline.

Fee-burning: где здесь Toncoin

Каждый перевод USD₮ платит газ в Toncoin. С учётом 150M+ пользователей и миллионов транзакций в месяц, накопительная нагрузка на Toncoin как gas-token заметна — часть этого Toncoin сжигается через стандартный механизм TON elections и переходит к валидаторам. Для экосистемы это создаёт рефлексивный эффект: чем активнее USD₮ обращается в TON, тем выше “сжигающее” давление на Toncoin. Это не специальный механизм USD₮ (никакого fee-burning хука в jetton-коде нет), а свойство всей TVM-модели — но именно USD₮ сделал его статистически значимым.

USD₮ Earn: yield через DeFi-vaults

В феврале 2026 TON Wallet добавил USD₮ Earn — возможность положить USD₮ в on-chain vaults и получать APY до 18%. Архитектурно это надстройка поверх стандартного Jetton: пользовательский USD₮ переводится через обычный TEP-74 transfer в адрес vault-контракта (Morpho-стиль lending + стратегии Re7), вместо USD₮ пользователь получает share-токен (тоже Jetton), который представляет долю в vault. Сам USD₮-контракт никак не знает о существовании Earn — vault это отдельный смарт-контракт, использующий стандартный интерфейс. Хороший пример того, что стандарт TEP-74 достаточен для построения сложных DeFi-продуктов поверх простого fungible-токена.

TIP

Чему учиться у USD₮

Когда будете деплоить собственный Jetton для серьёзного продукта: (1) сравнивайте газовую стоимость transfer с USD₮ — это honest baseline; (2) если нужны admin-возможности (заморозка, force-burn), смотрите на open-source stablecoin-contract от ton-blockchain как на reference; (3) decimals выбирайте исходя из совместимости с другими сетями, а не дефолта 9; (4) обязательно реализуйте TEP-89, иначе DeFi-протоколы не смогут вас интегрировать on-chain.


Чеклист деплоя Jetton

Перед деплоем в mainnet убедитесь:

  • Metadata (TEP-64) загружена и доступна по URI
  • Контракт протестирован в Sandbox (mint, transfer, burn)
  • Bounce-обработка работает корректно
  • Owner-only функции защищены проверкой ctx.sender == self.owner
  • Достаточно TON на адресе для деплоя (~0.1 TON)
  • Тест в testnet пройден успешно

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

  1. Не устанавливают metadata в формате off-chain (URL) или on-chain (Cell), и без метаданных токен не будет отображаться в кошельках и на маркетплейсах.
  2. Забывают передать достаточно TON для деплоя всех дочерних Jetton Wallet контрактов: каждый wallet требует баланс для оплаты хранения.
  3. Не ограничивают функцию mint: без проверки прав доступа любой может выпустить произвольное количество токенов.
  4. Используют жёстко закодированные decimals вместо стандартного значения 9, что приводит к несовместимости с DEX и кошельками.

Проверка знанийKnowledge check
Зачем при деплое Jetton Master указывается content: Cell с метаданными, если балансы хранятся в отдельных Jetton Wallet?
ОтветAnswer
Content (Cell с метаданными по TEP-64) хранит информацию о самом токене: название, символ, описание, иконка. Эти данные нужны кошелькам и эксплорерам для отображения токена. Jetton Wallet контракты хранят только балансы и ссылку на мастер-контракт -- метаданные они получают через get-метод Jetton Master.

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

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

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

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