Разработка смарт-контрактов на Move (Aptos/Sui)
Move появился как ответ на накопленные уязвимости EVM-экосистемы. Когда Solidity-контракт с reentrancy в 2016 году опустошил The DAO на $60M, стало понятно: модель глобального мутабельного стейта плюс произвольные внешние вызовы — это фундаментальная архитектурная проблема. Move решает её иначе: ресурсы не копируются и не уничтожаются неявно, они перемещаются. Отсюда и название языка.
Если вы пришли с опытом Solidity, первая неделя на Move будет болезненной. Не потому что сложно — а потому что многое, что Solidity позволяет по умолчанию, Move запрещает на уровне системы типов.
Чем Move отличается от Solidity на уровне безопасности
Linear type system и ресурсная модель
В Solidity токен — это запись в mapping: mapping(address => uint256) balances. Ничто не мешает написать функцию, которая создаст токены из воздуха или «забудет» вычесть баланс. Move-ресурс существует в одном месте одновременно — это гарантируется верификатором байткода, не аудитором.
// Aptos Move: ресурс нельзя скопировать или потерять
struct Coin<phantom CoinType> has store {
value: u64,
}
Атрибуты copy, drop, store, key — это возможности (abilities). Если у типа нет drop, компилятор не даст завершить функцию, не употребив значение. Забытый ресурс = ошибка компиляции, а не потерянные средства.
Апробированные векторы атак на EVM, закрытые в Move
| Вектор атаки (EVM) | Статус в Move |
|---|---|
| Reentrancy | Невозможен: нет произвольных внешних вызовов к неизвестным контрактам |
| Integer overflow | Невозможен: арифметика abort'ит при переполнении по умолчанию |
| Неправильная инициализация прокси | Значительно сложнее: storage model отличается |
| Access control через msg.sender | Заменён на signer — нельзя подделать |
| Selfdestruct | Нет аналога |
Это не значит, что Move-контракты не содержат уязвимостей. Логические ошибки никуда не делись. Но класс атак, который поглощает 60-70% EVM-аудита, в Move просто не существует.
Aptos vs Sui: архитектурные различия, которые влияют на разработку
Оба чейна используют Move, но модели хранения данных кардинально разные.
Aptos: глобальное хранилище и account-centric модель
В Aptos ресурсы хранятся в аккаунте: move_to(account, resource). Для доступа к ресурсу другого аккаунта нужен acquires-аннотированный вызов: borrow_global<T>(addr). Это похоже на EVM-паттерн с mapping(address => struct), но типобезопасно.
// Aptos: читаем ресурс конкретного аккаунта
public fun get_balance(owner: address): u64 acquires CoinStore {
borrow_global<CoinStore>(owner).coin.value
}
Параллелизм в Aptos реализован через Block-STM — оптимистичное исполнение с откатом при конфликтах. Это работает хорошо, если транзакции касаются разных аккаунтов, и плохо, если все пишут в один ресурс (например, глобальный счётчик).
Sui: объектная модель и настоящий параллелизм
В Sui всё — объект с уникальным ObjectID. Транзакция явно объявляет, какие объекты использует (owned, shared, immutable). Scheduler видит граф зависимостей заранее — транзакции с непересекающимися объектами исполняются параллельно без оптимистичных откатов.
// Sui: объект существует независимо от аккаунта
public struct NFT has key, store {
id: UID,
name: String,
// ...
}
public fun transfer_nft(nft: NFT, recipient: address, ctx: &mut TxContext) {
transfer::public_transfer(nft, recipient);
}
Для DeFi-протоколов с высоким TPS это принципиально. Shared objects — это shared мьютекс, они сериализуют транзакции. Хорошо спроектированный Sui-контракт максимизирует использование owned objects.
Наш процесс работы с Move
Toolchain и инфраструктура
Для Aptos используем Aptos CLI и Aptos Framework (Move стандартная библиотека). Для Sui — Sui CLI, Move Analyzer (LSP-плагин для VS Code). Тесты пишем на Move Test Framework (#[test], #[test_only]) с покрытием через aptos move test --coverage или sui move test.
Для форк-тестирования Aptos — Aptos Local Testnet через Docker. Для Sui — localnet режим Sui CLI. Полноценного аналога Hardhat mainnet fork пока нет, поэтому интеграционные тесты с реальными протоколами (Thala, Cetus, Turbos) требуют деплоя на testnet.
Типичные проблемы при первом контракте на Move
Generic type phantom. Начинающие часто путаются с phantom параметрами. Coin<USDC> и Coin<ETH> — разные типы, несмотря на одинаковую структуру. Это фича, не баг. Но если не пометить параметр как phantom, компилятор потребует его наличие в полях, что ломает абстракцию.
Ability constraints в generic функциях. Написать generic функцию для нескольких типов и не предусмотреть нужные ability-constraints — распространённая ошибка. T: store нужен для помещения в глобальное хранилище, T: copy + drop — для работы как value-type.
Event emission в Sui. В отличие от Aptos, где события хранятся в EventHandle ресурсе, в Sui события эмитируются через event::emit<T>() и не сохраняются on-chain. Если контракт предполагает реакцию на события из другого контракта — это невозможно. Нужна другая архитектура.
Как выглядит типичный проект
Аналитика (1-3 дня). Проектируем ресурсную модель: что хранится где, кто имеет signer, как передаются объекты. Часто меняем исходную архитектуру клиента — то, что работает в Solidity, в Move нужно переосмыслить.
Разработка (3-10 дней в зависимости от сложности). Пишем контракты, unit-тесты и интеграционные сценарии. Для DeFi-протоколов — обязательно fuzz-тестирование на edge cases с минимальными суммами и граничными значениями u64.
Аудит. Для Aptos используем Move Prover — формальный верификатор, который проверяет спецификации. Это уникальная возможность Move экосистемы: написать математически доказуемые инварианты контракта. Для Sui — Move Analyzer + ручной аудит.
Деплой. Для Aptos: aptos move publish с multi-sig через Aptos Safe. Для Sui: sui client publish с explicit upgrade capability management.
Ориентиры по срокам
Простой токен-контракт на Aptos (fungible asset стандарт) — 3-5 дней с тестами. Lending протокол с ценовыми оракулами и ликвидациями — 4-8 недель. Кросс-чейн бридж с проверками финальности — от 2 месяцев. Стоимость рассчитывается индивидуально после архитектурного брифинга.
Move — молодая экосистема с серьёзными техническими преимуществами. Инфраструктура для разработчиков отстаёт от EVM, документация местами устаревает быстрее, чем обновляется. Но если вам нужен протокол, где класс reentrancy-атак исключён на уровне языка — это именно то.







