The Sui Object Model
On Sui, all on-chain state is stored in objects.
28.1 Objects
On Sui, all on-chain state is stored in objects. An object is a struct with key whose first field is id: UID:
Every object has: a globally unique ID, a version (incremented on every mutation), an owner, its type, and its contents. Packages are themselves (immutable) objects.
public struct Sword has key, store {
id: UID,
damage: u64,
}28.2 UID and ID
Unique identity owned by exactly one object; required first field; anchors dynamic fields copy, drop, store
A plain copyable ID value (reference to an object) Because UID has no drop, you cannot destroy an object by accident: you must unpack it and delete its UID.
let id: UID = object::new(ctx); // fresh unique ID
let as_id: ID = id.to_inner(); // or object::uid_to_inner(&id)
let addr: address = id.to_address();
let obj_id: ID = object::id(&sword); // ID of any key object
id.delete(); // object::delete(id) — required to destroy an object28.3 Object lifecycle
module game::sword;
public struct Sword has key, store { id: UID, damage: u64 }
public fun forge(damage: u64, ctx: &mut TxContext): Sword {
Sword { id: object::new(ctx), damage }
}
public fun damage(self: &Sword): u64 { self.damage }
public fun upgrade(self: &mut Sword, amount: u64) { self.damage = self.damage + amount }
public fun destroy(self: Sword) {
let Sword { id, damage: _ } = self;
id.delete();
}28.4 Ownership kinds
Who can use it transfer::transfer / public_transfer Only the owner, as tx input Not required (fast path) Coins, NFTs, capabilities
transfer::share_object / public_share_object Required (sequenced) AMM pools, marketplaces, counters Immutable (frozen)
transfer::freeze_object / public_freeze_object Anyone, read-only Config, coin metadata, packages
Transfer to an object's address, or dynamic object field Via the parent Depends on parent Stored as a field inside another object Only through the wrapper n/a (not independently addressable) Items in an escrow Party objects
transfer::party_transfer (newer, 2025) Owner, but sequenced through consensus
Owned objects usable in consensus-ordered flows
28.5 Transactions and objects
A transaction declares the objects it uses as inputs. Transactions touching only owned/immutable objects can be finalized without full consensus ordering (fast path); transactions touching shared objects are ordered by consensus (Mysticeti). Two transactions writing the same shared object are serialized — design to avoid hot shared objects (Chapter 39).
28.6 TxContext
Take &TxContext if you only read, &mut TxContext if you create objects. It must be the last parameter.
public fun info(ctx: &mut TxContext) {
let sender: address = ctx.sender();
let epoch: u64 = ctx.epoch();
let epoch_ts: u64 = ctx.epoch_timestamp_ms(); // start of current epoch, coarse
let digest: &vector<u8> = ctx.digest();
let fresh = ctx.fresh_object_address(); // unique address
}