Object Display
This is a start of a conversation around Object Display proposal.
The questions it answers are:
- How objects should be displayed offchain?
- Should there be a (metadata) standard for displayable objects?
- Can we solve display problem for both utility objects (AdminCap) and art objects (DevnetNFT)?
- Is there a way to upgrade display of all objects in a centralized way?
- How to we solve data duplication in scenarios where objects don’t contain unique information (imagine Weapon Skins - image stays the same for X objects)?
Current state and background
Currently, every developer, if they wish to have their objects displayed nicely, is forced to add certain properties into their Move definitions. Such as “name” which has to be of type “String” (results in a name in the wallet) and “url” with type “Url” (treated as an image for an object).
struct MyNFT has key {
// ... custom app-specific fields ...
name: String,
url: Url
}
This approach is dead simple, but forces application developers to populate their assets with possibly repetitive data which usually results in a higher gas cost for creates / updates and a higher storage pricing. Additionally, if the Sui ecosystem agrees to add another property (say, “background_url”), packages published before that change would require republish / upgrades - which gets extremely expensive and logistically complicated in a decentralized system.
We believe that the main flow in this solution and similar ones is that we’re trying to solve “offchain” display with “onchain” object definitions. And by doing so, we lock ourselves in static “standards”, preemptive optimizations and inefficient practices in Move (and Smart Contracts in general).
Dynamic Configuration
With the recent Publisher
Object addition, it has become possible to prove that the account has published the type (and the package where this type is defined). Practically speaking, we opened a door for a centralized configuration that is specific for a type.
This improvement finally allowed us to propose the sui::display
module which aims to unify the way display properties are set; and the publisher (creator) of a package will have a tool and the power to define how their objects should look like.
So what is a Display object
It is an object with a very simple structure:
struct Display<phantom T: key> has key {
id: UID,
/// Contains `key => value` map with strings
fields: VecMap<String, String>,
}
The fields
property of the object contains a set of string keys and corresponding string values, which publisher can set at any time. They will contain the off-chain display configuration for the type T
specified in the type signature: Display<T>
(you can read it as “display-for-T”). Only publisher of the type T
has the permission (unlocked with the Publisher
Object) to create a new Display<T>
and modify its fields
.
Example: Alice publishes a new application - Capys; in her module she defines a type
Capy
and in the module initializer she creates aPublisher
Object. Using thatPublisher
Object she can call the functiondisplay::create_display<T>
whereT = Capy
. Done. The Display object for the typeCapy
is created.
How to use a Display object
Once Display Object is created, its fields can be set. For example, a pair "description" => "My Lovely Capy"
could be used as a key and a value. Later, for example, a pair "project_url" => "https://capy.art/"
could be added to the Display Object. There’s no limit to how many pairs could be added to the fields (the only limitation is the max size of a Sui Object).
The fields inside the Display should be treated as a “description” of a type, so if there’s a Display<Capy>
, we expect that all Capy
objects should have the same “description” and the same “project_url” field (and all the other possible fields defined in the Display - as long as they’re supported by the ecosystem).
// JSON example of Display<0x....::capy::Capy> contents
{
"description": "My Lovely Capy",
"project_url": "https://capy.art/"
}
However, most of the objects on Sui should contain some unique data (for example, an ID; or for an Art Objects - image URL), and if we were to solve the off-chain Display problem, we need to have a way of using the unique data stored in every Sui object and use them in the display definition. For that we suggest a string interpolation functionality.
Display pattern syntax
Every Sui object has at least one field (the required one is id
of type UID
), and these fields (and their values) often meant to affect the way the object is displayed. Let’s look at this example:
module my_bear_game::bears {
struct Bear has key {
id: UID,
level: u8,
ipfs: Url,
name: String
}
}
When showing this object in a wallet or in an explorer or in a marketplace application, we want to see most if not all of the properties defined here. But how do achieve that in a way that aligns with other objects? The field names might differ from application to application, the content might be different, lastly, we may want to have more fields - static ones - for example a “project_url” which will be the same for every Bear
but we don’t want to store it in every object of Bear
type - it’s simply expensive and inconvenient.
For all such cases we formed a pattern syntax for the Display. Simply put, it will be possible to insert a field of an object into a template string.
// JSON example of Display<my_bear_game::bears::Bear>
{
"name": "{name} (Level: {level})",
"image_url": "ipfs://{ipfs}/",
"description": "A bear. One of many",
"url": "https://sui-bears-game.xyz/bears/{id}",
"project_url": "https://sui-bears-game.xyz/",
}
In the example above you can see that some fields contain expressions inside curly braces: {...}
. They are meant to be substituted with the actual values of a specific object. So, for a Bear { name: "Billy", level: 10, ... }
the display “name” would be "name" => "Billy (Level: 10)"
, and if the properties changed, the result would also change.
However, it’s up to the creator to decide which properties to use and how to use them. In this dummy bear game some properties are static: such as “project_url” and “description”, and some use templates.
Another example that we find important to show here is an utility Object, display of which can also be configured by the publisher. Even though, currently, most of the objects of that kind are displayed rather poorly.
module marketplacers::market {
// Everyone can create a new Market; and AdminCap grants the
// permission to withdraw profits (eg from trading fees).
struct MarketplaceAdminCap {
id: UID,
market_id: ID
}
}
// JSON example of Display<marketplacers::market::MarketplaceAdminCap>
{
"name": "Marketplace Manager Capability",
"description": "Grants permission to manage Market #{market_id}",
"link": "https://markeplacers.xyz/admin/{market_id}",
"image_url": "https://marketplacers.xyz/cap.png",
"project_url": "https://marketplacers.xyz/"
}
Not only it improves the user experience by giving utility objects visuals, but also provides helpful links and tips for the owners.
Is there a list of Display properties I can use?
Short answer - there is no. The set of fields is meant to defined by the community and the actual usecases within the Sui ecosystem. The best way to think about it is as of NFT Metadata standards - they appeared, they evolved, they were changing from platform to platform; but unlike most of the metadata examples we know today, Display proposal does not set any restrictions to current nor further development. In other words - if some marketplace or a wallet X adds support for a feature “background_url”, creators are free to update the Display
for their objects to follow the trend; if ecosystem supports this addition and more actors enable it in their applications, this field de-facto becomes a part of the available set.
Nonetheless, we believe that it would be helpful to suggest a set of base properties which already exist (in that way or another) in the ecosystem:
-
name
- displayable name of an Object -
link
- link to an Object in an application / marketplace / explorer -
image_url
- the url to a displayable image of an Object -
project_url
- the link to the application (eg https://capy.art/) -
creator
- any string or URL that shows the creator
We believe this set would make the adaptation and the switching process simpler as well as help bootstrap most of the applications we see today.
Adoption and DevX
As platform creators we’re in the perfect position to integrate significant features like this one into the Full Node and its JSON RPC. Having a single place for consumers to get a Display for an object X is crucial for adoption and application performance. Tracking of the new display objects should also be performed by an indexer to make it accessible as fast as possible.
For the first step we consider adding an RPC method of kind (*actual name and arguments might change):
name: "sui_getDisplay"
params: [ objectId ]
returns: {} # JSON of the Display contents with pre-inserted values
It would free the wallet/explorer/ecosystem developers from the need to implement and support the Display syntax and lookup by giving them a single tool.
Feedback
It is important to mention that this is only the beginning of the conversation and we’re open to any feedback. Please, submit your questions and suggestions to this thread.