Skip to main content

Sui 对象显示

Sui 对象显示标准是一种模板引擎,可实现对类型的链上管理和链下表示(显示)。通过它,你可以将对象的数据替代为模板字符串。标准不限制你可以设置的字段。你可以使用 {property} 语法访问所有对象属性,然后将它们插入模板字符串中。

使用你拥有的 Publisher 对象为类型设置 sui::display。有关 Publisher 对象的更多信息,请参阅 Sui Move by Example 中的 Publisher 主题。

在 Sui Move 中,Display<T> 表示一个为类型 T 指定一组命名模板的对象。例如,对于类型 0x2::capy::Capy,显示语法为:Display<0x2::capy::Capy>

Sui 全节点通过匹配 Display 定义来处理类型 T 的所有对象,并在查询中使用 { showDisplay: true } 设置时返回处理结果。

显示属性

建议的基本属性集包括:

  • name - 对象的名称。用户查看对象时显示该名称。
  • description - 对象的描述。用户查看对象时显示该描述。
  • link - 在应用程序中使用的指向对象的链接。
  • image_url - 对象的图像的 URL 或 blob。
  • thumbnail_url - 用于在钱包、浏览器和其他产品中作为预览的 较小 图像的 URL。
  • project_url - 与对象或创建者相关联的网站链接。
  • creator - 表示对象创建者的字符串。

一个 Sui Hero 模块的示例

以下代码示例演示了根据类型 Heronameidimage_url 属性,Hero 模块的 Display 如何变化。以下表示 init 函数定义的模板:

{
"name": "{name}",
"link": "https://sui-heroes.io/hero/{id}",
"image_url": "ipfs://{image_url}",
"description": "A true Hero of the Sui ecosystem!",
"project_url": "https://sui-heroes.io",
"creator": "Unknown Sui Fan"
}
/// Example of an unlimited "Sui Hero" collection - anyone can
/// mint their Hero. Shows how to initialize the `Publisher` and how
/// to use it to get the `Display<Hero>` object - a way to describe a
/// type for the ecosystem.
module examples::my_hero {
use sui::tx_context::{sender, TxContext};
use std::string::{utf8, String};
use sui::transfer;
use sui::object::{Self, UID};

// The creator bundle: these two packages often go together.
use sui::package;
use sui::display;

/// The Hero - an outstanding collection of digital art.
struct Hero has key, store {
id: UID,
name: String,
image_url: String,
}

/// One-Time-Witness for the module.
struct MY_HERO has drop {}

/// In the module initializer one claims the `Publisher` object
/// to then create a `Display`. The `Display` is initialized with
/// a set of fields (but can be modified later) and published via
/// the `update_version` call.
///
/// Keys and values are set in the initializer but could also be
/// set after publishing if a `Publisher` object was created.
fun init(otw: MY_HERO, ctx: &mut TxContext) {
let keys = vector[
utf8(b"name"),
utf8(b"link"),
utf8(b"image_url"),
utf8(b"description"),
utf8(b"project_url"),
utf8(b"creator"),
];

let values = vector[
// For `name` one can use the `Hero.name` property
utf8(b"{name}"),
// For `link` one can build a URL using an `id` property
utf8(b"https://sui-heroes.io/hero/{id}"),
// For `image_url` use an IPFS template + `image_url` property.
utf8(b"ipfs://{image_url}"),
// Description is static for all `Hero` objects.
utf8(b"A true Hero of the Sui ecosystem!"),
// Project URL is usually static
utf8(b"https://sui-heroes.io"),
// Creator field can be any
utf8(b"Unknown Sui Fan")
];

// Claim the `Publisher` for the package!
let publisher = package::claim(otw, ctx);

// Get a new `Display` object for the `Hero` type.
let display = display::new_with_fields<Hero>(
&publisher, keys, values, ctx
);

// Commit first version of `Display` to apply changes.
display::update_version(&mut display);

transfer::public_transfer(publisher, sender(ctx));
transfer::public_transfer(display, sender(ctx));
}

/// Anyone can mint their `Hero`!
public fun mint(name: String, image_url: String, ctx: &mut TxContext): Hero {
let id = object::new(ctx);
Hero { id, name, image_url }
}
}

使用对象显示

调用 display::new<T> 可以创建一个 Display,可以在自定义函数或模块初始化器中创建,也可以作为可编程交易的一部分。 以下代码示例演示了如何创建一个 Display

module sui::display {
/// Get a new Display object for the `T`.
/// Publisher must be the publisher of the T, `from_package`
/// check is performed.
public fun new<T>(pub: &Publisher): Display<T> { /* ... */ }
}

创建了 Display 之后,你可以对其进行修改。以下代码示例演示了如何修改一个 Display

module sui::display {
/// Sets multiple fields at once
public fun add_multiple(
self: &mut Display,
keys: vector<String>,
values: vector<String>
) { /* ... */ }

/// Edit a single field
public fun edit(self: &mut Display, key: String, value: String) { /* ... */ }

/// Remove a key from Display
public fun remove(self: &mut Display, key: String ) { /* ... */ }
}

接下来,update_version 调用应用更改并通过触发事件设置 TDisplay。全节点接收事件并使用事件中的数据检索类型的模板。

以下代码示例演示了如何使用 update_version 调用:

module sui::display {
/// Update the version of Display and emit an event
public fun update_version(self: &mut Display) { /* ... */ }
}

Sui 实用对象

在 Sui 中,实用对象允许对功能进行授权。几乎所有模块都具有只能通过所需功能访问的功能。通用模块允许每个应用程序使用一个功能,例如市场。某些功能标记链上共享对象的所有权,或从另一个账户访问共享数据。 使用功能时,提供对象的有意义描述是重要的,以便促进用户界面的实现。这有助于避免在对象相似时意外传输错误的对象。它还为用户看到的项目提供了用户友好的描述。

以下示例演示了如何创建一个 capy 功能:

module capy::utility {
/// A capability which grants Capy Manager permission to add
/// new genes and manage the Capy Market
struct CapyManagerCap has key, store {
id: UID }
}

Sui 实用对象

在 Sui 中,实用对象允许对功能进行授权。几乎所有模块都具有只能通过所需功能访问的功能。通用模块允许每个应用程序使用一个功能,例如市场。某些功能标记链上共享对象的所有权,或从另一个账户访问共享数据。 使用功能时,提供对象的有意义描述是重要的,以便促进用户界面的实现。这有助于避免在对象相似时意外传输错误的对象。它还为用户看到的项目提供了用户友好的描述。

以下示例演示了如何创建一个 capy 功能:

module capy::capy_items {
/// A wearable Capy item. For some items there can be an
/// unlimited supply. And items with the same name are identical.
struct CapyItem has key, store {
id: UID,
name: String
}
}

具有动态表示的唯一对象

Sui Capys 使用动态图像生成。当一个 Capy 诞生时,其属性确定了 Capy 的外观,如颜色或图案。当用户将物品放在 Capy 上时,Capy 的外观会发生变化。当用户在 Capy 上放置多个物品时,有机会获得物品组合的奖励。

为了实现这一点,Capy 游戏 API 服务会在用户发起更改时刷新图像。Capy 的 URL 是一个带有 capy.id 的模板。但是,存储完整的 URL - 以及由于它们的多样化人口而在 Capy 对象中存储其他字段 - 还会导致用户支付过多的存储和增加的 gas 费用。

以下示例演示了如何实现动态图像生成:

module capy::capy {
/// A Capy - very diverse object with different combination
/// of genes. Created dynamically + for images a dynamic SVG
/// generation is used.
struct Capy has key, store {
id: UID,
genes: vector<u8>
}
}

具有唯一静态内容的对象

这是最简单的情景 - 一个对象代表着它自己的一切。将元数据标准应用于这种类型的对象非常容易,特别是如果对象永远保持不可变。然而,如果元数据标准发展,一些生态系统项目为某些属性添加新功能,这个对象始终保持在其原始形式,并可能需要向后兼容的更改。

module sui::devnet_nft {
/// A Collectible with a static data. URL, name, description are
/// set only once on a mint event
struct DevNetNFT has key, store {
id: UID,
name: String,
description: String,
url: Url,
}
}