본문으로 건너뛰기

Object Display V2 문법

Display V2는 Sui Move object value를 사람이 읽을 수 있는 string, JSON 또는 encoded representation으로 render하기 위한 template language이다. Display object는 key와 value가 모두 format string인 key-value pair set을 정의한다.

Format string 형식

format string은 literal text와 {}로 구분된 expression의 조합이다.

Hello, {name}! You have {balance} SUI.

literal brace를 출력하려면 두 번 쓴다. {{{를, }}}를 생성한다.

Expression 표현식

{...} 안의 각 expression은 3개 부분으로 구성된다.

{ chain | alternate | ... : transform }
^^^^^ ^^^^^^^^^ ^^^^^^^^^
primary fallbacks output format
  • Chain: |로 구분되며 left-to-right로 evaluate된다. 첫 번째 non-null result가 사용된다.
  • Transform: value가 render되는 방식을 제어하는 optional output format이며 : prefix를 사용한다.

모든 chain이 null로 evaluate되면 전체 format string은 null로 evaluate된다.

Access chain 접근 경로

chain은 object data 안으로 navigate한다. root object 또는 literal value에서 시작한 뒤 accessor sequence를 따른다.

root를 생략하면 chain은 현재 표시 중인 object에서 시작한다. $self로 해당 root object를 명시적으로 참조할 수도 있다.

{name}                  — equivalent to {$self.name}
{$self} — the root object itself
{$self.id} — explicit access from the root object

Field access 접근

dot notation으로 named field에 접근한다.

{name}                  — top-level field "name"
{inner.value} — nested field
{url.url.bytes} — deeply nested field

Positional access 접근

numeric index로 unnamed(positional) field에 접근한다.

{pos.0}                 — first positional field
{tuple.0.1} — nested positional access

vector 및 array indexing

[...]를 사용해 vector 또는 VecMap 안으로 index한다.

{items[0u64]}           — first element
{items[2u32]} — third element (any numeric type works)
{items[idx]} — use another field's value as the index
{items[ns[0u64]]} — nested: use element of `ns` as index into `items`

VecMap<K, V>의 경우 index는 key와 match된다.

{scores[6u32]}          — look up key 6u32 in the VecMap, returns the value

Dynamic field access (->) 접근

onchain storage에서 dynamic field를 load한다.

{parent->['key']}       — dynamic field with string key 'key'
{parent->['key'].x} — access field 'x' on the loaded dynamic field value
{parent.id->['key']} — explicit UID access (equivalent if parent starts with UID)
{parent->[field]} — use a field's evaluated value as the dynamic field name

-> access는 budget에서 object load 1개를 사용한다.

Dynamic object field access (=>) 접근

dynamic object field를 load한다(value는 full Sui object).

{parent=>['key']}       — dynamic object field with string key 'key'
{parent=>['key'].x} — access field 'x' on the loaded object
{parent.id=>['key'].id} — load the object and read its UID

=> access는 object load 2개를 사용한다(wrapper Field 1개, object 1개).

Derived object access (~>) 접근

parent object와 derived key를 사용해 onchain storage에서 derived object를 load한다.

{parent~>['key']}       — derived object with string key 'key'
{parent~>['key'].x} — access field 'x' on the loaded object
{parent.id~>['key'].id} — load the object and read its UID
{registry~>[$self]} — use the current object as the derived key

~> access는 budget에서 object load 1개를 사용한다.

Literal value 값

Literal은 expression root로 나타날 수 있으며 dynamic field, dynamic object field, derived object lookup의 key로도 사용할 수 있다.

Scalar 값

SyntaxTypeExample
$selfCurrent object{$self.id}
true or falsebool{true}
42u8u8{255u8}
1000u16u16{1000u16}
100000u32u32{100000u32}
999u64u64{999u64}
123u128u128{123u128}
42u256u256{42u256}
@0x1abcaddress{@0x1abc}

42 같은 bare number는 유효하지 않다. type suffix가 항상 필요하다.

String 값

SyntaxDescription
'hello'UTF-8 string (0x1::string::String)
b'hello'byte string(UTF-8 encoded bytes, vector<u8>)
x'48656c6c6f'hex byte string(vector<u8>)

string은 single quote를 사용한다. 개별 byte에는 .bytes[i](string literal의 경우) 또는 direct indexing [i](byte literal의 경우)로 접근한다.

{'ABC'.bytes[0u64]}     — 65 (ASCII 'A')
{b'ABC'[1u64]} — 66 (ASCII 'B')

Vector 값

{vector[1u8, 2u8, 3u8]}             — inferred element type
{vector<u64>[5u64, 6u64]} — explicit element type
{vector<u32>} — empty vector with type annotation

Struct literal 값

positional field:

{0x1::m::MyStruct(42u64, 'hello')}
{0x2::table::Table<address, u64>(a, b)} — with type parameters

named field:

{0x1::m::MyStruct { id: @0x123, value: 42u64 }}

enum 및 variant literal

{0x1::option::Option<u64>::Some#1(42u64)}          — variant name + index
{0x1::m::MyEnum::Variant#0(field1, field2)} — positional fields

variant name 뒤의 #N suffix는 variant의 discriminant index이다.

Alternate(fallback) 처리

fallback chain을 제공하려면 |를 사용한다. 첫 번째 chain이 null(missing field, out-of-bounds index 등)을 반환하면 다음 alternate를 시도한다.

{name | 'Unknown'}              — fall back to literal string
{preferred_name | name} — try preferred_name first
{a | b | c} — try three options in order
{bar | 42u64} — fall back to numeric literal

모든 alternate가 실패하면 expression은 null로 evaluate되고 전체 format string은 null이 된다.

Transform 변환

transform은 :transform syntax로 final value에 적용된다. value가 output으로 serialize되는 방식을 제어한다.

str (default)

value를 사람이 읽을 수 있는 string으로 render한다. transform을 지정하지 않으면 이것이 default이다.

{amount}           — same as {amount:str}
{amount:str} — explicit
Value typeOutput
u8-u256decimal number: 42
booltrue or false
address0x-prefixed canonical hex: 0x0000...1234
String or vector<u8>UTF-8 decoded string

hex

value를 lowercase hexadecimal로 render하며 type width에 맞춰 zero-pad한다.

{value:hex}
Value typeOutput
u82 characters: 2a
u164 characters: 002a
u328 characters: 0000002a
u6416 characters: 000000000000002a
u12832 characters
u25664 characters
address64 characters(0x prefix 없음)
vector<u8>각 byte를 2 hex character로

base64

value의 byte representation을 Base64-encode한다.

{value:base64}                  — standard base64, with padding
{value:base64(nopad)} — without padding
{value:base64(url)} — URL-safe alphabet
{value:base64(url, nopad)} — URL-safe, without padding

bcs

전체 value를 BCS-serialize한 뒤 결과를 base64-encode한다. base64와 같은 modifier를 지원한다.

{value:bcs}                     — BCS bytes, standard base64
{value:bcs(url, nopad)} — BCS bytes, URL-safe base64 without padding

이는 str 또는 hex로 render할 수 없는 aggregate type(struct, enum, vector)에 유용하다.

json

value를 structured JSON value(quoted string 아님)로 출력한다. expression이 format string의 유일한 content일 때만 유효하다.

{inner:json}
Value typeJSON output
booltrue or false
u8, u16, u32Number: 42
u64, u128, u256String: "42"
addressString: "0x0000...1234"
StringString: "hello"
vector<u8>String(base64): "AQID"
structObject: { "field": value, ... }
enumObject: { "@variant": "Name", "field": value, ... }
vectorArray: [v1, v2, ...]

timestamp (alias: ts)

numeric value를 Unix millisecond로 해석하고 ISO 8601로 format한다.

{created_at:ts}                 — "2023-04-12T17:00:00Z"
{1681318800000u64:ts} — literal timestamp

i64에 들어가는 numeric type에서만 동작한다.

url

str과 유사하지만 reserved URL character를 percent-encode한다(RFC 3986 unreserved set: A-Z a-z 0-9 - . _ ~).

{label:url}                     — "hello%20world" for "hello world"

numeric 및 address type은 reserved character가 없으므로 변경 없이 pass through된다.

Option auto-unwrapping 처리

Option<T> value는 자동으로 unwrap된다. option이 Some(v)이면 access가 v 안으로 계속된다. None이면 chain은 null로 evaluate된다.

{maybe_name}       — null if None, the string value if Some("...")

Enum variant field access 접근

enum에서 field에 접근할 때 current variant가 요청된 field를 가질 때만 access가 성공한다. 그렇지 않으면 null로 evaluate된다. 이를 통해 하나의 Display definition이 여러 variant를 자연스럽게 처리할 수 있다.

enum Status {
Pending { message: String },
Active { progress: u32 },
Done { count: u128, timestamp: u64 },
}
("pending",  "message = {message}")
("active", "progress = {progress}")
("complete", "count = {count}, timestamp = {timestamp}")

Pending value의 경우 "pending" key만 output을 생성한다. 나머지는 null이다.

Escaped brace 처리

output에 literal { 또는 }를 포함하려면 두 번 쓴다.

{{{ns[0u8]}, {ns[1u16]}, {ns[2u8]}}}

이는 {2, 1, 0}을 출력한다. outer {{}}는 literal brace이고, inner {...}는 expression이다.

Display object structure 구조

Display object는 (key, value) format string pair의 list이다. key와 value 모두 format string으로 parse되므로 computed key를 허용한다.

("name",        "{name}")
("description", "{desc | 'No description'}")
("image_url", "https://example.com/images/{image_id}")
("balance", "{amount:hex}")
("data", "{inner:json}")

결과(주어진 object에 대해):

{
"name": "My NFT",
"description": "A cool NFT",
"image_url": "https://example.com/images/42",
"balance": "000000000000002a",
"data": { "x": 100, "y": 200 }
}

key는 unique하고 non-null string으로 evaluate되어야 한다. evaluate에 실패한 value는 null을 생성한다(partial failure 허용).

Limit 제한

parsing과 evaluation은 configurable limit로 제한된다.

LimitDefaultDescription
max_depth32parsing 중 maximum nesting depth
max_nodes32,768Display의 모든 format string에 걸친 maximum AST node
max_loads8maximum object load(->는 1, =>는 2)

limit를 초과하면 error(TooDeep, TooBig, TooManyLoads)가 생성된다.

Formal grammar 정의

format   ::= strand*

strand ::= text | expr

text ::= part+
part ::= TEXT | '{{' | '}}'

expr ::= '{' chain ('|' chain)* (':' xform)? '}'

chain ::= (literal | IDENT) accessor*

accessor ::= '.' IDENT
| '.' NUM_DEC
| '[' chain ']'
| '->' '[' chain ']'
| '=>' '[' chain ']'
| '~>' '[' chain ']'

literal ::= self | address | bool | number | string | vector | struct | enum

self ::= '$' 'self'

address ::= '@' (NUM_DEC | NUM_HEX)
bool ::= 'true' | 'false'
number ::= (NUM_DEC | NUM_HEX) numeric
string ::= ('b' | 'x')? STRING
vector ::= 'vector' '<' type ','? '>' ('[' ']')?
| 'vector' ('<' type ','? '>')? array
array ::= '[' chain (',' chain)* ','? ']'
struct ::= datatype fields
enum ::= datatype '::' (IDENT '#')? NUM_DEC fields

fields ::= '(' chain (',' chain)* ','? ')'
| '{' named (',' named)* ','? '}'
named ::= IDENT ':' chain

type ::= 'address' | 'bool' | 'vector' '<' type '>' | numeric | datatype
datatype ::= NUM_HEX '::' IDENT ('<' type (',' type)* ','? '>')?
numeric ::= 'u8' | 'u16' | 'u32' | 'u64' | 'u128' | 'u256'

xform ::= 'str' | 'hex' | 'json' | 'timestamp' | 'url'
| 'base64' xmod?
| 'bcs' xmod?
xmod ::= '(' b64mod (',' b64mod)* ','? ')'
b64mod ::= 'url' | 'nopad'

Error reference 참조

ErrorCause
UnexpectedTokenparsing 중 unexpected token으로 인한 syntax error
UnexpectedEosunexpected end of input으로 인한 syntax error
TooBigAST node budget(max_nodes) 초과
TooDeepnesting depth(max_depth) 초과
TooManyLoadsobject load budget(max_loads) 초과
TooMuchOutputrendering 중 output size limit 초과
TransformInvalidtransform을 value type에 적용할 수 없음(예: struct에 :hex)
VectorTypeMismatchvector literal의 mixed element type