Розроблення смарт-контрактів на 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.
Для fork-тестування 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-атак виключен на рівні мови — це саме то.







