Post
Share your knowledge.

Mastering Sui’s Object Model & Ownership (Fixing Concurrency, Locking, and “Why Won’t My Call Work?
Problem this solves: New Sui devs often struggle with ownership kinds (owned, shared, immutable), why some calls are instant while others need consensus, and cryptic errors about locks or capabilities. This guide explains the model and shows how to fix the most common design mistakes.
The Mental Model
Everything is an object. Each has a unique ID and type.
Ownership types:
Owned: Exactly one owner (an address or another object). Mutations can often bypass consensus via fast-path because there’s no contention.
Shared: Many actors can access; updates require ordering via consensus.
Immutable: Frozen forever after creation; cheap to read, no writes allowed.
Concurrency superpower: Independent owned objects can be updated in parallel, letting Sui scale horizontally without global locks.
Step-by-Step: Designing with Ownership
Step 1 — Start Owned, Not Shared If you’re building a game inventory, marketplace listing draft, or personal profile, use owned objects first. You’ll get faster execution and fewer conflicts.
Step 2 — Promote to Shared Only When Needed Turn an object shared only if many unrelated users must mutate it concurrently (e.g., an orderbook, a public counter, an auction you want many bidders to touch). Expect to pay the consensus cost.
Step 3 — Use Immutables for Config & Code Protocol parameters, metadata templates, and published packages live best as immutable: once set, no accidental edits, and they’re cheap to reference everywhere.
Step 4 — Split & Merge Coins Correctly SUI coins are objects too. When you need exact gas or payments, use the CLI or SDK to split a coin into the desired amounts, pay, then merge dust back later. This avoids “too many small coin objects” clutter that confuses gas selection.
Split 10 SUI from a larger coin:
sui client split-coin --coin-id <COIN_ID> --amounts 10000000000 --gas-budget 30000000
Merge two coins:
sui client merge-coin --primary <COIN_A> --coin-to-merge <COIN_B> --gas-budget 30000000
Step 5 — Capabilities: Who’s Allowed to Mutate? Model authority explicitly. Give the creator an OwnerCap or AdminCap object that must be presented to mutate sensitive state. This removes guesswork and magic lists.
module myapp::caps { use sui::object; use sui::tx_context::{TxContext}; use sui::transfer;
public struct AdminCap has key, store { id: object::UID }
public entry fun mint_admin(ctx: &mut TxContext) {
let cap = AdminCap { id: object::new(ctx) };
transfer::transfer(cap, tx_context::sender(ctx));
}
// Later functions will require &AdminCap or its presence.
}
Step 6 — Avoid Hidden Shared Dependencies If an entry function touches a shared object anywhere in its call graph, the whole transaction follows the shared-object path (consensus). Keep hot loops on owned data only.
Step 7 — Deterministic IDs Where Appropriate When you need predictable references (e.g., one global registry per package), consider deriving IDs from seeds or creating them once and marking immutable. Avoid accidental duplicates by guarding creation with a capability.
Fixing the Top Ownership Errors
- ObjectNotFound
You’re calling with an object ID from a different network, or it was deleted, or your address doesn’t own it anymore.
Fix: Verify env; re-query the object; check its owner field.
- ObjectLocked or “object busy”
You attempted two transactions that both try to mutate the same owned object before the first finalized.
Fix: Wait for the first to finalize; use different owned objects for parallelism; batch operations in one programmable transaction if appropriate.
- Insufficient Permissions / capability required
You forgot to pass the cap object or the function signature requires a ref to it.
Fix: Redesign APIs to make capabilities explicit in parameters; ensure the signer actually owns the cap.
- MoveAbort with code related to EWrongOwner or ENotShared
Your function expects a shared object (or vice versa).
Fix: Ensure the object was converted to shared during initialization; document expected ownership in comments and types.
- Too many mutable references or borrow checker complaints
You’re trying to hold &mut to multiple objects in ways Move disallows for safety.
Fix: Refactor so you mutate one object at a time or pass by value where appropriate.
Patterns That Scale
Sharded State via Owned Buckets: Instead of one giant shared ledger, give each user a personal ledger (owned) and aggregate reads off-chain or via periodic reducers.
Event-Driven Analytics: Emit events rather than writing heavy on-chain aggregates; indexers consume events for dashboards.
Immutable Templates + Owned Instances: Store core logic or metadata immutable; users instantiate owned copies that they mutate freely.
Design Review Checklist
[ ] Can this be owned instead of shared?
[ ] Are capabilities explicit and passed as params?
[ ] Are we batching related writes to minimize lock contention?
[ ] Do we merge coin dust after complex flows?
[ ] Did we test under parallel load with multiple signers?
Locking down these habits eliminates 80% of early Sui headaches and lets you fully exploit Sui’s parallel execution.
- Sui
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
- How to Maximize Profit Holding SUI: Sui Staking vs Liquid Staking616
- Why does BCS require exact field order for deserialization when Move structs have named fields?65
- Multiple Source Verification Errors" in Sui Move Module Publications - Automated Error Resolution55
- Sui Move Error - Unable to process transaction No valid gas coins found for the transaction419
- Sui Transaction Failing: Objects Reserved for Another Transaction410