Архитектурные Anti-patterns на TON
Anti-pattern 1: Монолитный контракт
Ошибка: Один контракт хранит все данные и всю логику приложения.
[NO] Монолит:
TokenContract:
- mapping: 1M пользователей → балансы
- все transfers → одна точка обработки
- storage fee за 1M записей → огромная
[OK] Sharded (Jetton pattern):
MasterContract: supply, metadata
WalletContract_1: баланс user_1
WalletContract_2: баланс user_2
...
WalletContract_N: баланс user_N
Проблема: Монолитный контракт = один шард = bottleneck. Все транзакции обрабатываются последовательно. Storage fee растёт пропорционально числу пользователей.
Решение: Sharded pattern — данные каждого пользователя в отдельном контракте. Шарды обрабатывают их параллельно.
Anti-pattern 2: Синхронное мышление
Ошибка: Проектировать как Ethereum — ожидать мгновенный ответ от другого контракта.
[NO] Синхронное мышление:
function swap(tokenA, tokenB, amount) {
balance = tokenA.balanceOf(msg.sender); // ← Это невозможно на TON!
tokenA.transferFrom(msg.sender, amount); // ← Нельзя вызвать и ждать результат
tokenB.transfer(msg.sender, computed_amount);
}
[OK] Асинхронное:
// Шаг 1: User → DEX: "хочу swap" + отправляет Token A
recv_internal(msg) {
if (op == op::swap_request) {
// Сохранить pending swap в state
save_pending_swap(sender, amount, min_output);
// Отправить запрос на проверку баланса (async)
send(jetton_wallet, op::transfer, amount, dex_address);
}
}
// Шаг 2: DEX получает Token A → рассчитывает → отправляет Token B
recv_internal(msg) {
if (op == op::transfer_notification) {
// Token A получен, рассчитать курс
output = calculate_output(received_amount);
// Отправить Token B пользователю (ещё один async message)
send(tokenB_wallet, op::transfer, output, user_address);
}
}
Проблема: На TON нет synchronous calls. Каждое взаимодействие — async message. Попытка «вызвать и ждать» приведёт к design, который не может работать.
Anti-pattern 3: Игнорирование bounce
Ошибка: Отправлять сообщения без обработки bounce, надеясь, что всё пройдёт.
[NO] Без bounce handler:
send(recipient, value, body, mode);
// Если recipient не существует или бросил исключение
// → средства потеряны навсегда
[OK] С bounce handler:
send(recipient, value, body, mode | SEND_BOUNCE);
recv_internal(msg) {
if (msg.bounced) {
// Обработать ошибку: вернуть средства, обновить state
refund(original_sender);
}
}
Проблема: Без bounce handler — lost funds. Это самая дорогая ошибка на TON.
Anti-pattern 4: Unbounded storage
Ошибка: Контракт хранит неограниченно растущие данные (логи, история, все ордера).
[NO] Unbounded:
// Каждый swap добавляет запись в историю
self.history.push(swap_record);
// storage fee растёт → контракт "голодает" → freeze
[OK] Bounded:
// Хранить только последние N записей или агрегаты
self.total_volume += amount;
self.total_swaps += 1;
// Полная история — в off-chain indexer
Проблема: Storage fee пропорционально размеру state. Unbounded growth = контракт со временем потеряет весь баланс на storage fees и будет заморожен.
Anti-pattern 5: Hardcoded gas values
Ошибка: Жёстко закодированные значения gas forward.
[NO] Hardcoded:
send(target, ton("0.05"), body, mode);
// Что если 0.05 TON недостаточно для обработки?
// Что если gas prices изменятся?
[OK] Dynamic:
// Рассчитать gas на основе текущих conditions
int fwd_fee = get_forward_fee(cells, bits);
int compute_fee = estimated_compute();
int total = fwd_fee + compute_fee + SAFETY_MARGIN;
send(target, total, body, mode);
Проблема: Gas prices могут меняться через network config update. Hardcoded values сломаются.
Чек-лист архитектурного ревью
Перед деплоем системы проверьте:
| Категория | Проверка | Критичность |
|---|---|---|
| Sharding | Контракты sharding-friendly? Нет монолитов? | Critical |
| Async | Message flow корректен? State machine для multi-step? | Critical |
| Bounce | Все messages с bounce:true? Bounce handlers есть? | Critical |
| Storage | State bounded? Storage fee рассчитан? | High |
| Gas | Gas forward достаточен? Не hardcoded? | High |
| Replay | query_id для идемпотентности? | High |
| Access | Только authorized senders? | Critical |
| Overflow | Integer overflow проверки? | High |