Sui Move Development
Overview
Sui is a Layer 1 blockchain developed by Mysten Labs using the Move language. Sui Move differs significantly from Aptos Move: Sui uses an object-centric model, while Aptos uses an account-centric model. AI systems frequently confuse the two — this skill helps you generate correct Sui Move code.
⚠️ Gotchas (Most Common AI Mistakes)
1. Sui Object Model vs Aptos Resource Model
❌ Wrong (Aptos-style, using global storage):
module my_addr::counter {
struct Counter has key {
value: u64,
}
public entry fun increment(account: &signer) acquires Counter {
let c = borrow_global_mut<Counter>(signer::address_of(account));
c.value = c.value + 1;
}
}
✅ Correct (Sui object style, object passed as argument):
module my_pkg::counter {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
public struct Counter has key {
id: UID,
value: u64,
}
public entry fun increment(counter: &mut Counter, _ctx: &mut TxContext) {
counter.value = counter.value + 1;
}
}
2. Object Creation Requires object::new and Must Be Returned to the Caller
❌ Wrong (object not returned):
public entry fun create(ctx: &mut TxContext) {
let c = Counter { id: object::new(ctx), value: 0 };
// object is dropped — this will fail to compile
}
✅ Correct (transfer to sender):
use sui::transfer;
use sui::tx_context;
public entry fun create(ctx: &mut TxContext) {
let c = Counter { id: object::new(ctx), value: 0 };
transfer::transfer(c, tx_context::sender(ctx));
}
3. Use @mysten/sui Instead of the Legacy @mysten/sui.js
❌ Legacy (deprecated):
import { JsonRpcProvider } from '@mysten/sui.js';
const provider = new JsonRpcProvider('https://fullnode.mainnet.sui.io');
✅ Current version:
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
4. Signing and Submitting Transactions
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
const client = new SuiClient({ url: getFullnodeUrl('testnet') });
const keypair = new Ed25519Keypair();
const tx = new Transaction();
tx.moveCall({
target: `${PACKAGE_ID}::counter::increment`,
arguments: [tx.object(COUNTER_ID)],
});
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
Object Ownership Types
- Owned Object (
transfer::transfer): owned by a single address, most common - Shared Object (
transfer::share_object): accessible by anyone, requires consensus - Frozen Object (
transfer::freeze_object): immutable, readable by anyone
Installation
npm install @mysten/sui
# CLI
cargo install --locked --git https://github.com/MystenLabs/sui.git --branch testnet sui
sui client new-env --alias testnet --rpc https://fullnode.testnet.sui.io:443
Reference
Feedback
If this skill contains incorrect or outdated information, call:
id: sui/move-dev name: Sui Move Development version: 1.0.0 ecosystem: sui type: technical-doc time_sensitivity: evergreen source: community confidence: medium maintainer: AgentRel Community last_updated: 2026-03-19 feedback_endpoint: https://agentrel.vercel.app/api/feedback
Overview
Sui is a Layer 1 blockchain developed by Mysten Labs using the Move language. Sui Move differs significantly from Aptos Move: Sui uses an object-centric model, while Aptos uses an account-centric model. AI systems frequently confuse the two — this skill helps you generate correct Sui Move code.
⚠️ Gotchas (Most Common AI Mistakes)
1. Sui Object Model vs Aptos Resource Model
❌ Wrong (Aptos-style, using global storage):
module my_addr::counter {
struct Counter has key {
value: u64,
}
public entry fun increment(account: &signer) acquires Counter {
let c = borrow_global_mut<Counter>(signer::address_of(account));
c.value = c.value + 1;
}
}
✅ Correct (Sui object style, object passed as argument):
module my_pkg::counter {
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
public struct Counter has key {
id: UID,
value: u64,
}
public entry fun increment(counter: &mut Counter, _ctx: &mut TxContext) {
counter.value = counter.value + 1;
}
}
2. Object Creation Requires object::new and Must Be Returned to the Caller
❌ Wrong (object not returned):
public entry fun create(ctx: &mut TxContext) {
let c = Counter { id: object::new(ctx), value: 0 };
// object is dropped — this will fail to compile
}
✅ Correct (transfer to sender):
use sui::transfer;
use sui::tx_context;
public entry fun create(ctx: &mut TxContext) {
let c = Counter { id: object::new(ctx), value: 0 };
transfer::transfer(c, tx_context::sender(ctx));
}
3. Use @mysten/sui Instead of the Legacy @mysten/sui.js
❌ Legacy (deprecated):
import { JsonRpcProvider } from '@mysten/sui.js';
const provider = new JsonRpcProvider('https://fullnode.mainnet.sui.io');
✅ Current version:
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
const client = new SuiClient({ url: getFullnodeUrl('mainnet') });
4. Signing and Submitting Transactions
import { SuiClient, getFullnodeUrl } from '@mysten/sui/client';
import { Transaction } from '@mysten/sui/transactions';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
const client = new SuiClient({ url: getFullnodeUrl('testnet') });
const keypair = new Ed25519Keypair();
const tx = new Transaction();
tx.moveCall({
target: `${PACKAGE_ID}::counter::increment`,
arguments: [tx.object(COUNTER_ID)],
});
const result = await client.signAndExecuteTransaction({
signer: keypair,
transaction: tx,
});
Object Ownership Types
- Owned Object (
transfer::transfer): owned by a single address, most common - Shared Object (
transfer::share_object): accessible by anyone, requires consensus - Frozen Object (
transfer::freeze_object): immutable, readable by anyone
Installation
npm install @mysten/sui
# CLI
cargo install --locked --git https://github.com/MystenLabs/sui.git --branch testnet sui
sui client new-env --alias testnet --rpc https://fullnode.testnet.sui.io:443
Reference
Feedback
If this skill contains incorrect or outdated information, call: agentrel_feedback(skill="sui/move-dev", issue="<description>", code_snippet="<optional>", error_message="<optional>", fix="<optional>")