Deploying Smart Contracts on Sui: From Writing to Launch
We have been deploying smart contracts on Sui since mainnet launch — over 50 packages on various networks so far. Our experience: 10+ years in blockchain development, including Ethereum, Solana, and Sui. This guide covers how to migrate a dApp from EVM to Move, avoid common pitfalls, and launch a contract on mainnet.
Sui uses Move — a language developed at Facebook for Diem. Official Sui documentation: Move is a safe language for smart contracts. The key difference from Solidity: resources (objects) cannot be copied or accidentally destroyed — this is guaranteed by the type system at the compiler level. If you are used to EVM, the first days will be uncomfortable: forget about mappings address => uint256, here everything is built around objects with explicit owners.
Thanks to gas optimization, you can save up to 30% on fees, and a typical project pays for itself in 2 months.
Why is Move safer than Solidity?
In Solidity, vulnerabilities like reentrancy, incorrect address verification, or overflow are common headaches. Move solves this at the language level: objects cannot be copied (no dup), each object has a unique ID and owner. Even if a developer wants to write vulnerable code, the compiler will not allow it — for example, you cannot accidentally transfer an object to the wrong address without an explicit transfer::transfer call. This reduces the need for expensive audits, but does not eliminate them entirely.
Sui object model
In Sui, there is no global state in the classical sense. Everything is objects. Each object has a unique ID, version, and owner:
- Owned objects — belong to a specific address, only that address can use them in transactions
- Shared objects — accessible to everyone, but create contention and require consensus (slower)
- Immutable objects — frozen forever, accessible to everyone for reading
This is important when architecting: if your contract requires shared state (like an AMM with a common liquidity pool) — shared objects are inevitable and transactions go through consensus. If state can be split per user — use owned objects and get parallel processing without consensus.
Comparison of object types:
| Object type | Transaction speed | Gas cost | Contention risk |
|---|---|---|---|
| Owned | high | low | no |
| Shared | medium | medium | yes |
| Immutable | instant | zero (reading) | no |
What difficulties arise when migrating from EVM to Move?
First — abandoning mappings. Instead of mapping(address => uint256), you need to design objects with explicit owners. Second — understanding the init function: it is called once during deployment, analogous to a constructor. Third — getting used to the Capability pattern instead of msg.sender. Fourth — gas: Sui has no gas oracle, cost depends on object types (owned cheaper than shared). Fifth — debugging complexity: the local test framework is good, but production logging requires Tenderly-like solutions (Sui has Sui Explorer, but not as deep).
Tooling and project structure
Step-by-step guide to get started:
- Install Sui CLI from the official repository:
cargo install --locked --git https://github.com/MystenLabs/sui.git --branch mainnet sui - Create a new package and write code:
sui move new my_package sui move build sui move test The package structure includes Move.toml with dependency and address settings, modules in sources/, and tests in tests/.
Example Move.toml:
[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 — access control
In Move, there is no msg.sender like in Solidity. Permissions are passed through capability objects:
module my_package::admin { use sui::object::{Self, UID}; use sui::tx_context::TxContext; /// Admin capability — whoever holds the object is 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)) } /// Only the holder of AdminCap can call public fun privileged_action(_cap: &AdminCap, /* ... */) { // logic } } The init function is the entry point during deployment, analogous to a constructor. It is called automatically once.
Deploying a package
# Standard deployment or with offline signing sui client publish --gas-budget 100000000 --json sui client publish --gas-budget 100000000 --serialize-unsigned-transaction | sui keytool sign --address <ADDRESS> --data - After deployment, you get a packageId — the immutable address of the package. In transactions, you reference functions as <packageId>::<module>::<function>.
Upgradability
Sui supports package upgrades, but with restrictions. Upgrades are controlled via the UpgradeCap object. Command:
sui client upgrade --upgrade-capability <UPGRADE_CAP_ID> --gas-budget 100000000 Upgrade policies:
| Policy | Description |
|---|---|
| compatible | Can add functions, cannot change existing signatures |
| additive | Only adding new modules |
| dep_only | Only updating dependencies |
For production: transfer UpgradeCap to a timelock contract or multisig (Sui supports multisig via the MultiSig scheme). If no updates are planned, make UpgradeCap immutable via package::make_immutable.
Testing and inspection
Move has a built-in test framework that allows writing unit tests with transaction simulation. After deployment, verify objects through the blockchain explorer.
What our work on deploying smart contracts on Sui includes
We cover the full cycle: from auditing your code to deployment with monitoring.
- Audit and refactoring — checking for typical Move vulnerabilities, gas optimization
- CI/CD setup — automatic build, testing, and deployment via GitHub Actions
- Multisig integration — configuring UpgradeCap management via multisig or timelock
- Documentation — describing all functions, events, and objects
- Technical support — assistance in the first weeks after deployment
Evaluate your project — write to us. Order turnkey deployment and get a consultation from an engineer.
Checklist before mainnet deployment
- Tests via
sui move testwith coverage of edge cases - Gas budget check:
sui client dry-runbefore actual deployment -
UpgradeCaptransferred to multisig or frozen -
AdminCapand other privileged objects on a multisig address, not on an EOA - Verify that shared objects are truly necessary (owned is faster and cheaper)
- Review for typical Move vulnerabilities: missing
has keyabilities, incorrect transfer ownership







