Skip to main content
Sui’s ownership model is a key innovation that enables parallel transaction execution. By tracking ownership at the object level, Sui can process transactions without conflicts when they operate on different objects.

Ownership Types

Every object in Sui has one of four ownership types:

Owned

Owned by a single address

Shared

Shared among all users

Frozen

Immutable and permanently read-only

Wrapped

Contained within another object

Owned Objects

Objects owned by a single address can be used in transactions without consensus, enabling fast-path execution.

Creating Owned Objects

public fun create(value: u64, recipient: address, ctx: &mut TxContext) {
    transfer::public_transfer(
        Object { id: object::new(ctx), value },
        recipient,
    )
}

Transferring Ownership

Sui provides two transfer functions based on visibility requirements:
Use transfer::transfer for objects defined in your module:
/// Transfer ownership of `obj` to `recipient`. `obj` must have the `key` attribute,
/// which (in turn) ensures that `obj` has a globally unique ID.
/// This function has custom rules performed by the Sui Move bytecode verifier that ensures
/// that `T` is an object defined in the module where `transfer` is invoked.
public fun transfer<T: key>(obj: T, recipient: address) {
    transfer_impl(obj, recipient)
}
The distinction between transfer and public_transfer allows module authors to control how their objects can be transferred, enabling custom transfer logic and restrictions.

Fast-Path Execution

Owned objects enable Sui’s fastest transaction mode:
  1. No consensus required - Validators process independently
  2. Sub-second finality - Transactions complete in ~400ms
  3. Parallel execution - Multiple transactions can process simultaneously

Shared Objects

Shared objects are accessible by anyone but require consensus for modifications.

Creating Shared Objects

/// Turn the given object into a mutable shared object that everyone can access and mutate.
/// This is irreversible, i.e. once an object is shared, it will stay shared forever.
/// Aborts with `ESharedNonNewObject` of the object being shared was not created in this
/// transaction. This restriction may be relaxed in the future.
public fun public_share_object<T: key + store>(obj: T) {
    share_object_impl(obj)
}
Sharing an object is irreversible. Once shared, an object cannot be converted back to owned or frozen.

Shared Object Restrictions

From the source code:
/// Shared object operations such as wrapping, freezing, and converting to owned are not allowed.
const ESharedObjectOperationNotSupported: u64 = 4;
Shared objects cannot be:
  • Wrapped inside other objects
  • Frozen
  • Converted to owned objects

Transaction Arguments

Shared objects require special handling in transactions:
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
pub enum ObjectArg {
    // A Move object from fastpath.
    ImmOrOwnedObject(ObjectRef),
    // A Move object from consensus (historically consensus objects were always shared).
    // SharedObject::mutable controls whether caller asks for a mutable reference to shared object.
    SharedObject {
        id: ObjectID,
        initial_shared_version: SequenceNumber,
        // Note: this used to be a bool, but because true/false encode to 0x00/0x01, we are able to
        // be backward compatible.
        mutability: SharedObjectMutability,
    },
    // A Move object that can be received in this transaction.
    Receiving(ObjectRef),
}

Frozen Objects

Frozen objects are permanently immutable and can be read by anyone.

Freezing Objects

public fun freeze_object(o: Object) {
    transfer::public_freeze_object(o)
}
/// Freeze `obj`. After freezing `obj` becomes immutable and can no longer be transferred or
/// mutated.
/// The object must have `store` to be frozen outside of its module.
public fun public_freeze_object<T: key + store>(obj: T) {
    freeze_object_impl(obj)
}

Use Cases for Frozen Objects

Perfect for NFT metadata or token information that should never change:
fun init(witness: MY_COIN, ctx: &mut TxContext) {
    let (treasury, metadata) = coin::create_currency(
        witness,
        6,
        b"MY_COIN",
        b"",
        b"",
        option::none(),
        ctx,
    );
    // Freezing this object makes the metadata immutable
    transfer::public_freeze_object(metadata);
    transfer::public_transfer(treasury, ctx.sender())
}
Store protocol parameters or constants that should be globally readable but never modified.
Create immutable historical records or certificates.

Wrapped Objects

Wrapped objects exist inside other objects and are invisible to the outside world.

Wrapping and Unwrapping

public struct Wrapper has key {
    id: UID,
    o: Object,
}

public fun wrap(o: Object, ctx: &mut TxContext) {
    transfer::transfer(Wrapper { id: object::new(ctx), o }, ctx.sender());
}

#[lint_allow(self_transfer)]
public fun unwrap(w: Wrapper, ctx: &TxContext) {
    let Wrapper { id, o } = w;
    id.delete();
    transfer::public_transfer(o, ctx.sender());
}

Characteristics of Wrapped Objects

Not Directly Accessible

Cannot be used as transaction inputs

Version Unchanged

Version number doesn’t increment while wrapped

Storage Efficient

Counted as part of parent object’s storage

Secure Encapsulation

Only accessible through parent object’s API
While wrapped, objects cannot be accessed directly. You must unwrap them first or access them through their parent object.

Party Transfer (Advanced)

Sui supports party-based transfers for fine-grained access control:
/// Transfer ownership of `obj` to the `party`. This transfer behaves similar to both
/// `transfer` and `share_object`. It is similar to `transfer` in that the object is authorized for
/// use only by the recipient(s), in this case the `party`. This means that only the members
/// can use the object as an input to a transaction. It is similar to `share_object` two ways. One
/// in that the object can potentially be used by anyone, as defined by the `default` permissions of
/// the `Party` value. The other in that the object must be used in consensus and cannot be
/// used in the fast path.
public fun public_party_transfer<T: key + store>(obj: T, party: sui::party::Party) {
    assert!(party.is_single_owner(), EInvalidPartyPermissions);
    let (default, addresses, permissions) = party.into_native();
    party_transfer_impl(obj, default, addresses, permissions)
}

Receiving Objects

Sui supports receiving objects sent to other objects:
/// This represents the ability to `receive` an object of type `T`.
/// This type is ephemeral per-transaction and cannot be stored on-chain.
public struct Receiving<phantom T: key> has drop {
    id: ID,
    version: u64,
}

/// Given mutable (i.e., locked) access to the `parent` and a `Receiving` argument
/// referencing an object of type `T` owned by `parent` use the `to_receive`
/// argument to receive and return the referenced owned object of type `T`.
public fun public_receive<T: key + store>(parent: &mut UID, to_receive: Receiving<T>): T {
    let Receiving { id, version } = to_receive;
    receive_impl(parent.to_address(), id, version)
}

Ownership and Abilities

The store ability determines transferability:
Objects with store can be:
  • Transferred with public_transfer
  • Stored in other objects
  • Freely moved around
public struct Object has key, store {
    id: UID,
    value: u64,
}

Best Practices

  • Owned: For user-specific assets (NFTs, personal tokens)
  • Shared: For multi-user protocols (DEX pools, governance)
  • Frozen: For immutable configuration or metadata
  • Wrapped: For composition and encapsulation
Shared objects require consensus and are slower. Use owned objects when possible:
  • Split shared state into multiple owned objects
  • Use capability patterns for access control
  • Consider flash loans instead of direct shared access
These operations cannot be undone:
  • Sharing an object
  • Freezing an object
  • Deleting an object

Objects

Understand Sui’s object model

Dynamic Fields

Extend objects dynamically

Transactions

Learn about transaction structure

Build docs developers (and LLMs) love