In our previous article, we explored Move’s struct abilities (key, store, copy, drop), the foundation of secure smart contract development. Now, let’s level up and unlock Aptos Objects, the framework that powers everything from NFTs to fungible tokens.
This tutorial takes it even further by going deep into one of the most powerful concepts in Aptos Move: Objects. Move Objects provide a flexible and secure way to manage on-chain data, enabling developers to build more sophisticated and composable DApps. Whether you're looking to create advanced NFTs, manage complex game assets, or build intricate DeFi protocols, understanding Move Objects is crucial.
Composable DApps: DApps and protocols that can interact with each other seamlessly, much like Lego bricks. If data is structured as well-defined Objects with clear interfaces, one DApp can easily read from or write to another DApp's objects (with proper authorization), leading to a highly interconnected and innovative ecosystem. For instance, an NFT created in one DApp could be used as collateral in a lending protocol built by another team, precisely because it's a well-understood "Object."
By the end of this hands-on guide, you will have a comprehensive understanding of what Move Objects are, how they function, and how to apply them in your DApp development. We'll explore their core capabilities, walk through practical code examples, and discuss best practices to help you build smarter and more securely on Aptos.
Let's begin.
Understanding Move Objects
At its heart, Move is a "resource-oriented" programming language. Unlike traditional object-oriented languages where objects are merely data structures, in Move, resources are first-class citizens that cannot be copied or implicitly discarded; they can only be moved. This fundamental principle is extended and enhanced by Move Objects.
What are Move Objects?
In Aptos Move, an Object is a special kind of resource that acts as a container for other resources. It's safe to also view it as a smart contract account that can hold various assets and data structures, but crucially, it itself is a resource that can be owned and transferred. Imagine what flexibility this brings.
Key characteristics of Move Objects:
- Self-Contained Entities: Objects group related resources together, allowing them to be treated as a single, cohesive entity on the blockchain.
- Unique Address: Every object has its own unique address on the Aptos blockchain, derived from its creation. This address allows for direct interaction and referencing.
- Ownership: Objects have an owner field within their
ObjectCore
(which we'll discuss shortly). This owner can be an account address or even another object's address, enabling nested ownership structures. - Resource Container: Objects can own other resources. This means you can store custom data structs (defined with
has key
ability) directly under an object's address.
Objects have their own address and can own resources similar to an account. They are useful for representing more complicated data types on-chain as Objects can be used in
entry functions
directly, and can be transferred as complete packages instead of one resource at a time.
Key Features and Capabilities
Move Objects are designed with flexibility and security in mind, offering several powerful capabilities:
- Ownership: The owner field within an object's
ObjectCore
dictates who controls the object. This owner can be an external account or another object, enabling complex ownership hierarchies. - Transferability: By default, objects are transferable. However, this can be finely controlled. You can make an object:
- Ungated Transferable: Freely transferable by its owner.
- Untransferable: Cannot be transferred at all (e.g., for soulbound tokens).
- Controlled Transferable: Requires specific permissions or logic for transfer.
- Linear Transferable: Allows a one-time transfer by the owner.
- Extensibility: Objects can be designed to be extensible, meaning new resources can be added to their storage after creation. This is crucial for dynamic DApps where an object's properties might evolve over time (e.g., adding new traits to an NFT).
- Immutability: While objects can be extended, certain aspects of their configuration (like whether they are deletable or initially transferable) are set at creation time and cannot be changed later. This ensures predictable behavior.
- Deletion: Objects can be made "deletable" at creation. Deleting an object removes it from global storage and can even refund gas costs associated with its storage, promoting efficient resource management on-chain.
*Object Creation and Configuration *
Creating and configuring an object involves using specific functions and references provided by the aptos_framework::object
module.
The core of every object is the ObjectCore
struct, which is automatically added to any struct declared with has key
and used to create an object via object::create_object
, object::create_named_object
or object::create_sticky_object
, depending on the type of object you want to create.
struct ObjectCore has key {
/// Used by guid to guarantee globally unique objects and create event streams
guid_creation_num: u64,
/// The address (object or account) that owns this object
owner: address,
/// Object transferring is a common operation, this allows for disabling and enabling
/// transfers bypassing the use of a TransferRef.
allow_ungated_transfer: bool,
/// Emitted events upon transferring of ownership.
transfer_events: event::EventHandle<TransferEvent>,
}
This leads us to the types of objects there are.
- Normal Object
- Named Object
- Sticky Object
We’ll explain them in details later in the second part of this article article.
When you create an object, you receive a ConstructorRef
, that is, creating an object returns a ConstructorRef
, which is a one-time-use capability that allows you to configure the object during its initial setup.
struct ConstructorRef has drop {
self: address;
can_delete: bool; //true if object can be deleted. named objects are not deletable
}
This ConstructorRef
is vital for:
- Generating a Signer:
object::generate_signer(&constructor_ref)
allows the object itself to act as a signer, enabling it to own and manage its own resources. - Adding Initial Resources: Moving resources into the object's storage using
move_to(&object_signer, MyResource { ... })
. - Configuring Capabilities: Creating
TransferRef
,DeleteRef
, orExtendRef
from the ConstructorRef to define the object's behavior. We’ll explain these other refs shortly
Working with Objects in Move
Next, we’ll see the mechanics of interacting with Aptos move objects.
Object Address Generation
An object's address is generated when it's created. This ensures that each object has a unique identifier on the blockchain. The object::create_object function handles this generation and returns a ConstructorRef for the newly created object's address.
Object Storage
Objects, being resources themselves, are stored on the blockchain. When an object is created, its ObjectCore
is stored at its unique object address. Any additional resources that the object "owns" are also stored at this same object address. This centralizes all data related to an object under its specific on-chain address.
Object References and Abilities
Move's type system uses "abilities" to define how a struct can be used. For objects, these abilities are crucial:
-
has key
: This ability is essential for any struct that you want to store directly under an account's (or object's) address. The ObjectCore itself has key. -
has store
: This ability means a value of this type can be stored in global storage (i.e., on-chain). -
has copy
: Allows values of this type to be copied. -
has drop
: Allows values of this type to be implicitly dropped at the end of their scope.
We explained abilities in details in our last class, they also apply to objects.
Beyond the ConstructorRef
, the aptos_framework::object
module provides several other important references that grant specific capabilities:
DeleteRef
: Grants the ability to delete the object from storage. This reference can only be created at object construction time if the object is configured to be deletable.
struct DeleteRef has drop, store { self: address, }
ExtendRef
: Allows adding new resources to the object's storage after its initial creation. This is useful for dynamic objects.
struct ExtendRef has drop, store { self: address, }
TransferRef
: Provides control over the object's transferability. This reference can be used to disable or re-enable ungated transfers.
struct TransferRef has drop, store { self: address, }
LinearTransferRef
: A one-time-use reference derived from a TransferRef that allows for a single, controlled transfer of the object. This is useful for "soulbound" or single-transfer assets.
struct LinearTransferRef has drop { self: address, owner: address, }
DeriveRef
: Used to create "derived objects" from a given object, allowing for hierarchical object structures.
struct DeriveRef has drop, store { self: address, }
Conclusively, we've covered the core concepts of Aptos Move Objects, from their fundamental resource-oriented nature and key characteristics to their powerful features like ownership
, transferability
, and extensibility
. Understanding the ObjectCore
and the various Ref
types is crucial for configuring and interacting with objects.
Mastering Move Objects isn't just about learning a technical feature; it's about gaining the ability to design sophisticated, composable DApps on Aptos.
In the next part of this series, we'll look into practical code examples, demonstrating how to leverage Normal
, Named
, and Sticky
Objects to build smarter and more securely on Aptos. Stay tuned!
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.