Smart Contract Deployment in Sui
Sui uses Move — a language developed at Facebook for Diem. The key difference from Solidity: in Move, resources (objects) cannot be copied or accidentally destroyed — this is guaranteed by the type system at the compiler level. If you're familiar with EVM, the first few days will be uncomfortable: forget about address => uint256 mappings, everything here is built around objects with explicit ownership.
Sui's 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 they can use them in transactions
- Shared objects — available to everyone, but create contention and require consensus (slower)
- Immutable objects — permanently frozen, available to all for reading
This is important for architecture: if your contract requires shared state (like an AMM with a shared liquidity pool) — shared objects are inevitable and transactions go through consensus. If state can be distributed across users — use owned objects and get parallel processing without consensus.
Tooling and Project Structure
# Install Sui CLI
cargo install --locked --git https://github.com/MystenLabs/sui.git \
--branch mainnet sui
# Create a new package
sui move new my_package
# Build
sui move build
# Tests
sui move test
Package structure:
my_package/
├── Move.toml # dependencies, addresses
├── sources/
│ └── my_module.move
└── tests/
└── my_module_tests.move
Move.toml contains package addresses — in [addresses] you define aliases:
[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. Rights are passed through capability objects:
module my_package::admin {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
/// Administrative capability — whoever holds the object is admin
public struct AdminCap has key, store {
id: UID,
}
fun init(ctx: &mut TxContext) {
// Created once at deployment, passed to deployer
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
}
}
init function — entry point at deployment, analogous to constructor. Called once automatically.
Package Deployment
# Deploy to mainnet
sui client publish \
--gas-budget 100000000 \
--json
# With specific key (if multiple in keystore)
sui client publish \
--gas-budget 100000000 \
--serialize-unsigned-transaction | \
sui keytool sign --address <ADDRESS> --data -
After deployment, you get packageId — the immutable address of the package. In transactions, you reference functions as <packageId>::<module>::<function>.
Upgradeability
Sui supports package upgrades, but with restrictions. Upgrade is controlled through the UpgradeCap object:
| Policy | Description |
|---|---|
compatible |
Can add functions, cannot change existing signatures |
additive |
Only adding new modules |
dep_only |
Only updating dependencies |
sui client upgrade \
--upgrade-capability <UPGRADE_CAP_ID> \
--gas-budget 100000000
For production: pass UpgradeCap to a timelock contract or multisig (Sui supports multisig via MultiSig scheme). If upgrades are not planned — make UpgradeCap immutable via package::make_immutable.
Testing and Inspection
Move has a built-in test framework:
#[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);
}
}
After deployment, verify through Sui Explorer or Sui Vision — objects created during init will be visible in the deployment transaction.
Checklist Before Mainnet Deployment
- Tests via
sui move testwith edge case coverage - Gas budget check:
sui client dry-runbefore actual deployment -
UpgradeCappassed to multisig or frozen -
AdminCapand other privileged objects — on a multisig address, not on EOA - Verify that shared objects are actually needed (owned — faster and cheaper)
- Review for typical Move vulnerabilities: missing
has keyabilities, incorrect transfer ownership







