Програ ммирование безопасных смарт-контрактов
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.
В этом разделе мы рассмотрим несколько наиболее интересных особенностей блокчейна TON, а затем пройдемся по списку лучших практик для разработчиков, программирующих смарт-контракты на FunC.
Разделение контрактов
При разработке контрактов для EVM вы обычно разбиваете проект на несколько контрактов для удобства. В некоторых случаях можно реализовать всю функциональность в одном контракте, и д аже там, где разделение контрактов было необходимо (например, пары ликвидности в Automated Market Maker), это не привело к каким-либо особым трудностям. Транзакции выполняются в полном объеме: либо всё работает, либо всё возвращается.
В TON настоятельно рекомендуется избегать "неограниченных структур данных" и разделять один логический контракт на небольшие части, каждая из которых управляет небольшим объёмом данных. Основным примером является реализация TON Jettons. Это версия TON стандарта токенов ERC-20 от Ethereum. Вкратце, у нас есть:
- Один
jetton-minter
, который хранитtotal_supply
,minter_address
и пару ссылок: описание токена (метаданные) иjetton_wallet_code
. - И множество jetton-кошельков, по одному на каждого владельца этих жетонов. В каждом таком кошельке хранится только адрес владельца, его баланс, адрес jetton-minter и ссылка на jetton_wallet_code.
Это необходимо для того, чтобы передача жетонов происходила напрямую между кошельками и не затрагивала высоконагруженные адреса, что является основой для параллельной обработки транзакций.
То есть, приготовьтесь к тому, что ваш контракт станет "группой контрактов", и они будут активно взаимодействовать друг с другом.
Возможно частичное выполнение транзакций
В логике вашего контракта появляется новое уникальное свойство: частичное выполнение транзакций.
Например, рассмотрим поток сообщений стандартного TON Jetton:
Как следует из диаграммы:
- отправитель посылает сообщение
op::transfer
на свой кошелек (sender_wallet
); sender_wallet
уменьшает баланс токенов;sender_wallet
отправляет сообщениеop::internal_transfer
на кошелек получателя (destination_wallet
);destination_wallet
увеличивает свой баланс токенов;destination_wallet
отправляетop::transfer_notification
своему владельцу (destination
);destination_wallet
возвращает избыточный газ с сообщениемop::excesses
наresponse_destination
(обычноsender
).
Обратите внимание, если destination_wallet
не смог обработать сообщение op::internal_transfer
(произошло исключение или закончился газ), то этот этап и все последующие не будут выполнены. Однако первый шаг (уменьшение баланса в sender_wallet
) будет выполнен. Результатом будет частичное выполнение транзакции, что приводит к некорректному состоянию Jetton
и, в данном случае, потеря средств.
В худшем случае все токены могут быть украдены таким образом. Представьте, что вы сначала начисляете бонусы пользователю, а затем отправляете сообщение op::burn
на его кошелёк Jetton, но не можете гарантировать, что сообщение op::burn
будет успешно обработано.