Object Basics

Define Sui Object

In Sui Move, besides primitive data types, you can define organized data structures using struct. For example:

struct Color {
    red: u8,
    green: u8,
    blue: u8,
}

The struct defines a data structure to represent RGB color. You can use a struct like this to organize data with complicated semantics. However, an instance of a struct, such as Color, is not a Sui object yet. To define a struct that represents a Sui object type, you must add a key capability to the definition. The first field of the struct must be the id of the object with type UID from the object module - a module from the core Sui Framework.

use sui::object::UID;

struct ColorObject has key {
    id: UID,
    red: u8,
    green: u8,
    blue: u8,
}

The ColorObject represents a Sui object type that you can use to create Sui objects that can eventually be stored on the Sui network.

Important: In both core Move and Sui Move, the key ability denotes a type that can appear as a key in global storage. However, the structure of global storage is a bit different: core Move uses a (type, address)-indexed map, whereas Sui Move uses a map keyed by object IDs.

The UID type is internal to Sui, and you most likely won't need to deal with it directly. For curious readers, it contains the "unique ID" that defines an object on the Sui network. It is unique in the sense that no two values of type UID will ever have the same underlying set of bytes.

Create Sui Object

After you define a Sui object type you can create or instantiate a Sui object. To create a new Sui object from its type, you must assign an initial value to each of the fields, including id. The only way to create a new UID for a Sui object is to call object::new. The new function takes the current transaction context as an argument to generate unique IDs. The transaction context is of type &mut TxContext and should be passed down from an entry function. You can call Entry functions directly from a transaction.

To define a constructor for ColorObject

// object creates an alias to the object module, which allows you to call
// functions in the module, such as the `new` function, without fully
// qualifying, for example `sui::object::new`.
use sui::object;
// tx_context::TxContext creates an alias to the TxContext struct in the tx_context module.
use sui::tx_context::TxContext;

fun new(red: u8, green: u8, blue: u8, ctx: &mut TxContext): ColorObject {
    ColorObject {
        id: object::new(ctx),
        red,
        green,
        blue,
    }
}

Sui Move supports field punning, which allows you to skip the field values if the field name happens to be the same as the name of the value variable it is bound to. The preceding code example leverages this to write "red," as shorthand for "red: red,".

Store Sui Object

You now have a constructor for the ColorObject. If you call this constructor, it puts the value in a local variable. The local variable can be returned from the current function, passed to other functions, or stored inside another struct. The object can be placed in persistent global storage, be read by anyone, and accessed in subsequent transactions.

All of the APIs for adding objects to persistent storage are defined in the transfer module. One key API is:

public fun transfer<T: key>(obj: T, recipient: address)

This places obj in global storage along with the metadata that records recipient as the owner of the object. In Sui, every object must have an owner. The owner can be either an address, another object, or "shared". See Object ownership for more details.

A common use of this API is to transfer the object to the sender/signer of the current transaction, such as when you mint an NFT owned by you. The only way to obtain the sender of the current transaction is to rely on the transaction context passed in from an entry function. The last argument to an entry function must be the current transaction context, defined as ctx: &mut TxContext.

To obtain the current signer's address, you can call tx_context::sender(ctx).

The following code sample creates a new ColorObject and sets the owner to the sender of the transaction:

use sui::transfer;

// This is an entry function that you can call directly by a Transaction.
public entry fun create(red: u8, green: u8, blue: u8, ctx: &mut TxContext) {
    let color_object = new(red, green, blue, ctx);
    transfer::transfer(color_object, tx_context::sender(ctx))
}

You can also add a getter to ColorObject that returns the color values so that modules outside of ColorObject are able to read their values:

Note: Naming convention: Constructors are typically named new, which returns an instance of the struct type. The create function is typically defined as an entry function that constructs the struct and transfers it to the desired owner (most commonly the sender).

You can also add a getter to ColorObject that returns the color values so that modules outside of ColorObject are able to read their values:

public fun get_color(self: &ColorObject): (u8, u8, u8) {
    (self.red, self.green, self.blue)
}

Reference: https://github.com/MystenLabs/sui/blob/main/sui_programmability/examples/objects_tutorial/sources/color_object.move

Last updated