Sui上のオブジェクトは、その構造と動作が明示的であり、理解しやすい方法で表示できます。しかし、クライアントのためにより豊富なメタデータをサポートするために、Sui フレームワークで定義されているDisplayオブジェクトを使用して、効率的にオブジェクトを「記述する」標準的な方法があります。

背景

歴史的に、オブジェクトをユーザーインターフェースで表示できるように、標準的な構造に合意しようとする様々な試みがありました。一つのアプローチは、オブジェクト構造体内に特定のフィールドを定義し、それらが存在する場合にUIで使用するというものでした。このアプローチは柔軟性に欠け、開発者は全てのオブジェクトで同じフィールドを定義する必要があり、時にはそのフィールドがオブジェクトにとって意味をなさないこともありました。

/// 表示のためのオブジェクト構造を標準化する試み。
public struct CounterWithDisplay has key {
    id: UID,
    /// このフィールドが存在する場合、UIで`name`として表示されます。
    name: String,
    /// このフィールドが存在する場合、UIで`description`として表示されます。
    description: String,
    // ...
    image: String,
    /// オブジェクトの実際のフィールド。
    counter: u64,
    // ...
}

フィールドに静的データが含まれている場合、それは全てのオブジェクトで重複することになります。また、Moveにはインターフェースがないため、オブジェクトのタイプを「手動で」チェックしないと特定のフィールドがあるかどうかを知ることができず、クライアントのフェッチがより複雑になります。

オブジェクトディスプレイ

これらの問題に対処するため、Suiはオブジェクトを表示するための標準的な方法を導入しています。オブジェクト構造体にフィールドを定義する代わりに、表示メタデータは別のオブジェクトに格納され、タイプに関連付けられます。これにより、表示メタデータが重複せず、拡張や維持が容易になります。

Sui Displayのもう一つの重要な機能は、テンプレートを定義し、そのテンプレート内でオブジェクトのフィールドを使用できることです。これにより、より柔軟な表示が可能になるだけでなく、開発者が全てのオブジェクトで同じ名前と型の同じフィールドを定義する必要がなくなります。

Sui Fullnodeによってオブジェクトディスプレイがネイティブにサポートされており、オブジェクトタイプにDisplayが関連付けられている場合、クライアントは任意のオブジェクトの表示メタデータをフェッチできます。

module book::arena {
    use std::string::String;
    use sui::package;
    use sui::display;

    /// `Publisher`オブジェクトを要求するための一回限りの証人。
    public struct ARENA has drop {}

    /// 表示されるオブジェクトの例。
    public struct Hero has key {
        id: UID,
        class: String,
        level: u64,
    }

    /// モジュールイニシャライザーで`Publisher`オブジェクトを作成し、
    /// その後`Hero`タイプのDisplayを作成します。
    fun init(otw: ARENA, ctx: &mut TxContext) {
        let publisher = package::claim(otw, ctx);
        let mut display = display::new<Hero>(&publisher, ctx);

        display.add(
            b"name".to_string(),
            b"{class} (lvl. {level})".to_string()
        );

        display.add(
            b"description".to_string(),
            b"One of the greatest heroes of all time. Join us!".to_string()
        );

        display.add(
            b"link".to_string(),
            b"<https://example.com/hero/{id}>".to_string()
        );

        display.add(
            b"image_url".to_string(),
            b"<https://example.com/hero/{class}.jpg>".to_string()
        );

        // 新しいデータでディスプレイを更新します。
        // 変更を適用するために呼び出す必要があります。
        display.update_version();

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

作成者の特権

オブジェクトはアカウントによって所有され、真の所有権の対象となる可能性がありますが、Displayはオブジェクトの作成者によって所有されることができます。これにより、作成者は表示メタデータを更新し、全てのオブジェクトを更新する必要なく、変更をグローバルに適用できます。作成者はDisplayを別のアカウントに転送したり、メタデータを管理するためのカスタム機能を持つアプリケーションをオブジェクトの周りに構築したりすることもできます。

標準フィールド

最も広くサポートされているフィールドは以下の通りです:

最新のサポートされているフィールドのリストについては、Sui Documentationを参照してください。

標準的なフィールドのセットがありますが、Displayオブジェクトはそれらを強制しません。開発者は必要なフィールドを自由に定義でき、クライアントはそれらを適切に使用できます。一部のアプリケーションでは追加のフィールドが必要で、他のフィールドを省略する場合があり、Displayはそれらをサポートするのに十分な柔軟性があります。

Displayの操作

Displayオブジェクトはsui::displayモジュールで定義されています。これはファントムタイプをパラメータとして取るジェネリック構造体です。ファントムタイプはDisplayオブジェクトを記述するタイプと関連付けるために使用されます。DisplayオブジェクトのfieldsはキーバリューペアのVecMapで、キーはフィールド名、値はフィールド値です。versionフィールドは表示メタデータのバージョン管理に使用され、update_display呼び出し時に更新されます。

ファイル: sui-framework/sources/display.move

struct Display<phantom T: key> has key, store {
    id: UID,
    /// 表示用のフィールドを含みます。現在サポートされている
    /// フィールドは: name, link, image, descriptionです。
    fields: VecMap<String, String>,
    /// Publisherによって手動でのみ更新できるバージョン。
    version: u16
}