Частые ошибки проектирования TON-приложений — симптомы, причины и пошаговые решения.
Перенос монолитного паттерна из EVM: вся логика и состояние в одном контракте. В TON это создаёт единую точку сериализации — один аккаунт обрабатывает все транзакции последовательно, теряя преимущества шардирования.
В TON сообщения — асинхронные. Если получатель не существует или выбрасывает исключение, сообщение возвращается (bounce). Без обработки bounce-сообщений контракт теряет средства и остаётся в несогласованном состоянии.
Разработчик мыслит в терминах синхронных вызовов (как в Solidity), но в TON каждое сообщение — отдельная транзакция. Между отправкой и получением ответа состояние контракта может измениться другими сообщениями.
Контракт не проверяет msg_value при получении сообщения. Если пользователь отправил недостаточно TON для покрытия всей цепочки вычислений и исходящих сообщений, выполнение обрывается на полпути.
Использование одного большого HashMap/Dictionary в контракте для хранения всех пользовательских данных. В TON каждая операция с Dictionary имеет стоимость O(log n) по глубине дерева Cell, и весь контракт — единая точка сериализации.
В TON асинхронные сообщения затрудняют классические flash-loan атаки, но аналогичные атаки возможны через цепочки сообщений в одном блоке. Протокол полагается только на текущую цену без TWAP или проверки временного окна.
Контракты ссылаются друг на друга через захардкоженные адреса вместо registry-паттерна или deterministic addressing. При обновлении одного контракта вся цепочка зависимостей ломается.
Операции, требующие итерации по коллекции (batch-переводы, массовые обновления), не учитывают, что газ может закончиться на любой итерации. TVM не откатывает storage-изменения при out-of-gas.
Реализация токена по ERC-20 паттерну: один контракт хранит mapping(address => balance). В TON это уничтожает шардирование — все операции сериализуются через один аккаунт вместо распределения по шардам.
Контракт не использует seqno, query_id или другой механизм для отслеживания уже обработанных сообщений. Каждое входящее сообщение с одинаковым payload обрабатывается как новое.
Master-контракт проверяет sender_address, но не верифицирует, что отправитель — настоящий child-контракт с правильным init_state. В TON адрес вычисляется из init_state — проверка адреса без проверки кода недостаточна.
Данные упакованы без учёта структуры Cell tree в TVM. Неправильный порядок полей, избыточные Cell reference, неиспользование bits для маленьких значений — всё увеличивает число Cell и стоимость хранения.
Разработчик сфокусирован на функциональности контрактов, но не выстраивает off-chain мониторинг. Без наблюдения за балансами, throughput и error rate проблемы обнаруживаются слишком поздно.
При получении ответного сообщения контракт не проверяет exit_code или op-код ответа. Успешное получение сообщения не означает успешное выполнение операции на стороне получателя.
Схема данных спроектирована без учёта частоты доступа к полям и cost модели TVM. Глубокая вложенность Cell, неиспользуемые поля в hot path, отсутствие версионирования.
Контракт задеплоен без set_code/set_data механизма или governance для обновлений. В отличие от Ethereum proxy pattern, TON позволяет менять код контракта через SETCODE — но эту возможность нужно заложить при проектировании.
При проектировании цепочки A→B→C→D каждое звено потребляет gas + forward_fee. Разработчик не учёл кумулятивные затраты и не зарезервировал достаточно TON на старте цепочки.
Протокол полагается на единственный oracle-контракт для ценовых данных. Отсутствие агрегации, проверки freshness и fallback-механизма создаёт single point of failure.
Mini App разработан для happy path: все запросы к TON API и RPC успешны. Отсутствует обработка таймаутов, retry-логика и UI-индикация ошибок. Telegram Mini App работает в мобильном контексте с нестабильным интернетом.
Вся коллекция реализована как один контракт с Dictionary of items. Каждая операция (mint, transfer, burn) проходит через единственный аккаунт. TEP-62 NFT Standard предполагает отдельные контракты для каждого item.