Object Programming

Objects

In contrast to many other blockchains where storage is centered around accounts and each account contains a key-value store, Sui's basic storage unit is an object. Objects are the building blocks of programming in Sui Move, and they are characterized by having the following two properties:

  • All objects are annotated with the has key keyword following their struct declaration.

  • All objects have id: UID as their first field.

struct Car has key {
	id: UID,
	speed: u8,
	acceleration: u8,
	handling: u8
}

Object ownership can be classified using the following four methods:

Owned by an address: A Move object, upon creation in the Move code, can be transferred to an address. After the transfer, the address owns that object. An object owned by an address can be used (i.e. passed as a Move call parameter) only by transactions signed by that owner address. Owned objects can be passed as a Move call parameter in any of these three forms: read-only reference (&T), mutable reference (&mut T), and by-value (T).

struct ObjectA has key { id: UID }

public entry fun create_object_owned_by_an_address(ctx: &mut TxContext) {
	transfer::transfer({
		ObjectA { id: object::new(ctx) }
	}, tx_context::sender(ctx))
}

Owned by another object: When an object is owned by another object, it's not wrapped. The child object still exists independently as a top-level object and can be accessed directly in Sui storage. We will cover this in greater detail in the fourth session as we discuss Dynamic Fields.

struct ObjectB has key, store { id: UID }

public entry fun create_object_owned_by_an_object(parent: &mut ObjectA, ctx: &mut TxContext) {
	let child = ObjectB { id: object::new(ctx) };
	ofield::add(&mut parent.id, b"child", child);
}

Immutable: You can't mutate an immutable object, and an immutable object doesn't have an exclusive owner. Anyone can use an immutable object in a Move call. All Move packages are immutable objects: you can't change a Move package after you publish it. You can convert a Move object into an immutable object using the freeze_object operation. You can only pass an immutable object in Move calls as a read-only reference (&T).

struct ObjectD has key { id: UID }

public entry fun create_immutable_object(ctx: &mut TxContext) {
	transfer::freeze_object(ObjectD { id: object::new(ctx) })
}

Shared: An object can be shared, meaning that anyone can read or write this object. In contrast to mutable owned objects (which are single-writer), shared objects require consensus to sequence reads and writes. In other blockchains, every object is shared. However, Sui programmers often have the choice to implement a particular use-case using shared objects, owned objects, or a combination. This choice can have implications for performance, security, and implementation complexity.

struct ObjectC has key { id: UID }

public entry fun create_shared_object(ctx: &mut TxContext) {
	transfer::share_object(ObjectC { id: object::new(ctx) })
}

Functions

Several visibility modifiers limit or gate access to Sui Move functions. public functions allow other modules to import a particular function. public(friend) functions allow modules given explicit permission to import a particular function. entry functions allow a function to be called directly, such as in a transaction. A special function called init gets executed only once, when its associated module is published. It always has the same signature and only one argument: ctx: &mut TxContext.

Capabilities

A capability is a special pattern in Sui Move that gates access to certain functions. Capabilities can be used in conjunction with the init function to ensure that only one capability will ever exist, and that it will be sent to the module publisher.

struct AdminCapability has key { 
	id: UID 
}

fun init(ctx: &mut TxContext) {
	transfer::transfer(AdminCapability {
		id: object::new(ctx),
	}, tx_context::sender(ctx))
}

Last updated