Деплой смарт-контрактов в TON
TON — не EVM. Это принципиально другая архитектура, и опыт деплоя в Ethereum здесь не переносится. Контракты пишутся на FunC (или Tact — более высокоуровневый язык), компилируются в TVM bytecode, и взаимодействуют через асинхронные сообщения. Синхронного вызова contract.someFunction() не существует — только отправка message и ожидание ответного сообщения.
Язык и инструментарий
FunC vs Tact
FunC — низкоуровневый язык TON. Прямой контроль над TVM инструкциями, storage layout, gas. Сложен в освоении, но все оптимизации — в ваших руках. Стандартные контракты (Jetton, NFT) написаны на FunC.
Tact — более высокоуровневый, синтаксис ближе к TypeScript. Компилируется в FunC → TVM bytecode. Быстрее разрабатывать, меньше вероятность ошибок в storage serialization. Для новых контрактов — рекомендуемый выбор если нет специфических требований к оптимизации.
// Простой counter на Tact
contract Counter {
counter: Int as uint32;
init() {
self.counter = 0;
}
receive("increment") {
self.counter = self.counter + 1;
}
get fun value(): Int {
return self.counter;
}
}
Эквивалентный контракт на FunC занял бы втрое больше строк с явным управлением storage через store_uint / load_uint.
Blueprint: стандартный toolchain
Blueprint — официальный фреймворк разработки TON контрактов (аналог Hardhat для EVM):
npm create ton@latest my-project
cd my-project
npm install
Структура проекта:
contracts/ # .tact или .fc файлы
tests/ # Jest-based тесты
scripts/ # deploy скрипты
wrappers/ # TypeScript обёртки для контрактов
Компиляция и тесты
npx blueprint build # компиляция
npx blueprint test # запуск тестов
Тесты работают на локальном TVM эмуляторе (@ton/sandbox) — транзакции выполняются локально без обращения к сети, тесты быстрые:
import { Blockchain, SandboxContract, TreasuryContract } from '@ton/sandbox';
import { Counter } from '../wrappers/Counter';
describe('Counter', () => {
let blockchain: Blockchain;
let deployer: SandboxContract<TreasuryContract>;
let counter: SandboxContract<Counter>;
beforeEach(async () => {
blockchain = await Blockchain.create();
deployer = await blockchain.treasury('deployer');
counter = blockchain.openContract(await Counter.fromInit());
const deployResult = await counter.send(
deployer.getSender(),
{ value: toNano('0.05') },
{ $$type: 'Deploy', queryId: 0n }
);
expect(deployResult.transactions).toHaveTransaction({
from: deployer.address,
to: counter.address,
deploy: true,
});
});
it('should increment counter', async () => {
await counter.send(deployer.getSender(), { value: toNano('0.01') }, 'increment');
expect(await counter.getValue()).toBe(1n);
});
});
Деплой в testnet и mainnet
TON использует концепцию StateInit для деплоя: контракт деплоится отправкой сообщения с init (code + initial data) на адрес контракта. Адрес вычисляется детерминированно из hash(code, data) — вы знаете адрес до деплоя.
// scripts/deployCounter.ts
import { toNano } from '@ton/core';
import { Counter } from '../wrappers/Counter';
import { NetworkProvider } from '@ton/blueprint';
export async function run(provider: NetworkProvider) {
const counter = provider.open(await Counter.fromInit());
await counter.send(
provider.sender(),
{ value: toNano('0.05') }, // TON для gas + storage rent
{ $$type: 'Deploy', queryId: 0n }
);
await provider.waitForDeploy(counter.address);
console.log('Deployed at:', counter.address.toString());
}
# Testnet
npx blueprint run --testnet
# Mainnet
npx blueprint run --mainnet
Blueprint запросит seed phrase или подключение Tonkeeper через QR.
Storage rent — важная специфика TON
В отличие от Ethereum, TON взимает аренду за хранение данных в storage. Контракт с нулевым балансом будет «заморожен» (frozen) после ~6 месяцев, и данные удалены через несколько лет (uninit state). Для production контрактов нужно:
- Закладывать достаточный баланс при деплое (для простого контракта — 0.1-0.5 TON)
- Реализовать механизм пополнения баланса
- Мониторить баланс через TonAPI
Приблизительная стоимость хранения: 1 bit данных в год = 0.000000001 TON × 365 × ... Практически: контракт с 1KB storage потребляет ~0.015 TON/год.
Jetton (ERC-20 аналог) и NFT
Стандартные токены в TON реализованы через шаблонные контракты TEP-74 (Jetton) и TEP-62 (NFT). Архитектура радикально отличается от ERC-20:
В ERC-20 один контракт хранит маппинг всех балансов. В TON каждый holder имеет свой отдельный контракт — JettonWallet. Основной JettonMaster контракт только минтит токены и хранит глобальную информацию. Transfer — это сообщение от JettonWallet отправителя к JettonWallet получателя.
Это означает: адрес Jetton кошелька для конкретного holder-а нужно вычислять через getWalletAddress(ownerAddress) на JettonMaster контракте. Нельзя просто использовать адрес Jetton мастера для переводов.
Blueprint предоставляет готовые обёртки для стандартных Jetton и NFT контрактов от TON Foundation — не нужно писать с нуля.
Верификация контракта
TON верификатор — verifier.ton.org. Загружаете исходники и proof-of-compilation, верификатор подтверждает соответствие bytecode. После верификации tonviewer.com и tonscan.org показывают исходный код и позволяют вызывать getter функции.
Ориентиры по срокам
Деплой готового Tact/FunC контракта в testnet + mainnet с верификацией — от нескольких часов. Разработка кастомного контракта с нуля + тесты + деплой: простой контракт — 1-2 дня, контракт со сложной бизнес-логикой (AMM, стейкинг, NFT маркетплейс) — 1-2+ недели.







