Деплой смарт-контрактов в Sui
Sui использует Move — язык, разработанный в Facebook для Diem. Ключевое отличие от Solidity: в Move ресурсы (objects) не могут быть скопированы или случайно уничтожены — это гарантируется системой типов на уровне компилятора. Если вы привыкли к EVM, первые дни будут неудобными: забудьте о маппингах address => uint256, здесь всё строится вокруг объектов с явным владельцем.
Объектная модель Sui
В Sui нет глобального стейта в классическом понимании. Всё — объекты. Каждый объект имеет уникальный ID, version и owner:
- Owned objects — принадлежат конкретному адресу, только он может их использовать в транзакциях
- Shared objects — доступны всем, но создают contention и требуют консенсус (медленнее)
- Immutable objects — навсегда заморожены, доступны всем для чтения
Это важно при архитектуре: если ваш контракт требует shared state (как AMM с общим пулом ликвидности) — shared objects неизбежны и транзакции идут через консенсус. Если state можно разнести по пользователям — используйте owned objects и получаете параллельную обработку без консенсуса.
Инструментарий и структура проекта
# Установка Sui CLI
cargo install --locked --git https://github.com/MystenLabs/sui.git \
--branch mainnet sui
# Создание нового пакета
sui move new my_package
# Сборка
sui move build
# Тесты
sui move test
Структура пакета:
my_package/
├── Move.toml # зависимости, адреса
├── sources/
│ └── my_module.move
└── tests/
└── my_module_tests.move
Move.toml содержит адреса пакетов — в [addresses] задаёте алиасы:
[package]
name = "my_package"
version = "0.0.1"
edition = "2024.beta"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet" }
[addresses]
my_package = "0x0"
Capability pattern — управление доступом
В Move нет msg.sender как в Solidity. Права передаются через объекты-capability:
module my_package::admin {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
/// Административный capability — кто держит объект, тот и admin
public struct AdminCap has key, store {
id: UID,
}
fun init(ctx: &mut TxContext) {
// Создаётся один раз при деплое, передаётся деплоеру
transfer::transfer(AdminCap { id: object::new(ctx) }, tx_context::sender(ctx))
}
/// Только держатель AdminCap может вызвать
public fun privileged_action(_cap: &AdminCap, /* ... */) {
// logic
}
}
init функция — точка входа при деплое, аналог constructor. Вызывается один раз автоматически.
Деплой пакета
# Деплой в mainnet
sui client publish \
--gas-budget 100000000 \
--json
# С конкретным ключом (если несколько в keystore)
sui client publish \
--gas-budget 100000000 \
--serialize-unsigned-transaction | \
sui keytool sign --address <ADDRESS> --data -
После деплоя получаете packageId — неизменяемый адрес пакета. В транзакциях ссылаетесь на функции как <packageId>::<module>::<function>.
Апгрейдимость
Sui поддерживает апгрейды пакетов, но с ограничениями. Апгрейд контролируется через UpgradeCap объект:
| Политика | Описание |
|---|---|
compatible |
Можно добавлять функции, нельзя менять существующие сигнатуры |
additive |
Только добавление новых модулей |
dep_only |
Только обновление зависимостей |
sui client upgrade \
--upgrade-capability <UPGRADE_CAP_ID> \
--gas-budget 100000000
Для production: передайте UpgradeCap в timelock-контракт или multisig (Sui поддерживает multisig через MultiSig схему). Если обновления не планируются — сделайте UpgradeCap immutable через package::make_immutable.
Тестирование и инспекция
Move имеет встроенный тест-фреймворк:
#[test_only]
module my_package::tests {
use sui::test_scenario;
#[test]
fun test_mint() {
let mut scenario = test_scenario::begin(@admin);
{
// setup
};
test_scenario::next_tx(&mut scenario, @user);
{
// assertions
};
test_scenario::end(scenario);
}
}
После деплоя верифицируйте через Sui Explorer или Sui Vision — объекты, созданные при init, будут видны в транзакции деплоя.
Чеклист перед mainnet-деплоем
- Тесты через
sui move testс покрытием edge cases - Проверка gas budget:
sui client dry-runперед фактическим деплоем -
UpgradeCapпередан в multisig или заморожен -
AdminCapи другие privileged objects — на multisig-адресе, не на EOA - Проверка что shared objects действительно нужны (owned — быстрее и дешевле)
- Ревью на типичные Move-уязвимости: missing
has keyabilities, некорректный transfer ownership







