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 object

28.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
}