Learn about Sui’s ownership model and how it enables parallel execution and fast transaction finality.
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.
Sui provides two transfer functions based on visibility requirements:
Module-Private
Public Transfer
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)}
Use transfer::public_transfer for objects with store ability:
/// Transfer ownership of `obj` to `recipient`. `obj` must have the `key` attribute,/// which (in turn) ensures that `obj` has a globally unique ID./// The object must have `store` to be transferred outside of its module.public fun public_transfer<T: key + store>(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.
/// 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 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),}
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)}
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)}
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)}