Sui.

Допис

Діліться своїми знаннями.

Винагорода+15

Xavier.eth.
Jun 17, 2025
Питання та відповіді експертів

Як обмеження здібностей взаємодіють з динамічними полями в гетерогенних колекціях?

Я створюю ринок, який повинен обробляти кілька типів активів з різними вимогами до здібностей, і я зіткнувся з деякими фундаментальними запитаннями щодо системи типів Move. Я хочу зберігати різні типи активів в одній колекції, але вони мають різні здібності:

  • Звичайні NFT: key + store(можна передавати)
  • Токени Soulbound: key тільки (не передаються)
  • Користувальницькі активи з обмеженнями передачі
public struct Marketplace has key {
    id: UID,
    listings: Bag,  // Want to store different asset types here
}

// This works for transferable assets
public fun list_transferable<T: key + store>(
    marketplace: &mut Marketplace,
    asset: T,
    price: u64
) { /* ... */ }

// But how to handle soulbound assets?
public fun list_soulbound<T: key>(  // No store ability
    marketplace: &mut Marketplace,
    asset_ref: &T,  // Can only take reference
    price: u64
) { /* How do I store metadata about this? */ }

Ключові питання:

  • Вимоги до здібностей: dynamic_field::add<K, V>()При використанні Vзавжди потрібно під store час компіляції? Чи можуть типи обгортки обійти це?
  • Гетерогенне зберігання: Чи може одна сумка зберігати об'єкти з різними наборами здіб key + store + copyностей (vskey + store) та обробляти їх по-різному під час виконання?
  • Безпека типу: Оскільки динамічні поля виконують стирання типу, як я можу підтримувати безпеку типу під час отримання значень? Який шаблон для зберігання метаданих типу?
  • Шаблон свідків: Як працюють обмеження здібностей з фантомними типами? Чи можу я зберігати Asset<Type1>і Asset<Type2>в тій самій колекції та витягувати інформацію про тип пізніше?

Побудова системи, де NFT, токени, пов'язані з душею, та обмежені активи потребують функціональності ринку, але з різною семантикою передачі.

Я спробував типи обгортки, кілька колекцій на набір можливостей, окреме зберігання метаданих типу. Кожен з них має компроміси між безпекою типу, витратами на газ та складністю.

  • Sui
  • Architecture
0
5
Поділитися
Коментарі
.

Відповіді

5
harry phan.
Jun 18 2025, 03:58

Ви стикаєтесь із ядром системи типів та здібностей Move, і ви абсолютно праві, коли піднімаєте ці питання, розробляючи гетерогенний ринок.

По-перше, про обмеження можливостей: будь-яке значення, яке ви зберігаєте за допомогою dynamic_field: :add<K, V> (), повинно мати можливість зберігання під час компіляції. Це жорстке правило в Move: значення без сховища просто не можуть зберігатися в сховищі, будь то в динамічних полях, таблицях або подібних конструкціях. Спроба загорнути тип, що не є магазином, у структуру також не спрацює - тут переміщення є суворим. Якщо навіть в одному полі не вистачає магазину, вся обгортка також втрачає магазин. Тож, на жаль, тут немає обгортки. Ресурси, прив'язані до душі з лише ключем (і без магазину), виключаються з будь-якої колекції, яка записує до глобального сховища, як-от мішок або динамічне поле. Це добре задокументовано в документах про здібності переміщення.

Що стосується зберігання різних типів активів в одній колекції: якщо всі вони реалізують key + store, то так, один Bag може зберігати їх, але під капотом Move не робить реального поліморфізму. Тип повинен бути відомий під час доступу, а Bag повинен бути однорідним з точки зору компілятора. Це призводить до вашого наступного питання: як ми підтримуємо безпеку типу в динамічних полях? Оскільки Move виконує стирання типів для цих колекцій, він не пам'ятає, який саме тип знаходиться всередині — вам потрібно надати відображення. Одним із поширених шаблонів є загортання кожного ресурсу в структуру, яка включає метадані, як-от enum u8 або TypeName, тому, коли ви отримуєте об'єкт, ви перевіряєте метадані та відповідно зменшуєте або обробляєте. Немає чарівного RTTI; ви підробляєте це за допомогою дисципліни даних.

Тепер про фантомні типи та шаблони свідків - вони корисні для безпеки під час компіляції, але не дають вам самоаналізу під час виконання. Отже, так, ви можете створити актив і змінювати T, але кожна інстанція активу все одно повинна відповідати обмеженням можливостей колекції. Якщо ви хочете зберігати як активи, так і активи в одному місці, обидва вони повинні реалізувати store, а SoulBoundToken, ймовірно, не буде. Вам потрібно буде обробляти їх по-різному — можливо, використовуючи посилання або зберігаючи лише метадані для непередаваних активів, замість того, щоб намагатися зберігати об'єкт.

У дизайні вашого ринку це означає, що ви, ймовірно, розглядаєте гібридну стратегію. Для звичайних NFT та інших переданих активів ви можете вільно використовувати динамічні поля об'єктів або сумки, якщо вони відповідають ключу + магазину. Для ресурсів, пов'язаних із душею, які є лише ключовими, найкраще зберігати посилання та пов'язані метадані в паралельному пакеті метаданих або використовувати спеціальний тип лістингу, який не зберігає сам актив, а записує його існування за допомогою ідентифікатора та додаткових даних. Це складніше, але це дає вам гнучкість для належного застосування семантики передачі.

Посилання

  1. https://github.com/MystenLabs/sui/blob/c7ec9546978b3c52b0c57bbdb9693f5068dd3383/external-crates/move/documentation/book/src/abilities.md
  2. https://github.com/sui-foundation/sui-move-intro-course/blob/92bb4986ad91c5ffd01c10c5b0d3bbbfa9d12507/unit-four/lessons/2_dynamic_fields.md
1
Найкраща відповідь
Коментарі
.
Owen.
Owen4587
Jun 23 2025, 14:33

1. Чи завжди потрібні значення в динамічних поляхstore?

dynamic_field::addЯкщо ви хочетезберегтизначення безпосередньо в динамічному полі (наприклад, viastore), воно повинно задовольняти всі здібності, необхідні для типу поля - включаючи. Якщо Vйого немаєstore, ви не можете помістити його безпосередньо в динамічне поле.

Але ось хитрість:

Виможете обгортатакі типи всередині іншої структури, яка маєstore, навіть якщо внутрішній тип цього не має.

Приклад:

struct AssetWrapper<T: key> has store {
    inner: T,
}

Незважаючи на те, що Tтільки маєkey, AssetWrapper<T>можна зберігати, тому що він сам єstore.

Тепер ви можете зробити:

dynamic_field::add(&mut object, key, AssetWrapper { inner: soulbound_token });

Це дозволяє зберігати як передавальні, так і непередавані активи під однією абстракцією.


2. Чи може одна сумка зберігати предмети з різними наборами здібностей?

Коротка відповідь:так, використовуючи обгортки та методи стирання типів. Але є і компроміси.

У програмі «Переміщення» ви не можете безпосередньо мати список декількох типів конкретів, якщо вони не загорнуті в загальний контейнер.

Хороший підхід - використовувати обгортку, як:

struct AnyAsset has store {
    // Maybe include metadata or witness type info
    data: Vector<u8>,     // serialized representation?
    type_info: TypeInfo, // Type metadata
}

Або ще краще, використовуйте кодування enum у варіантному стилі (якщо підтримується вашим діалектом Move):

enum AssetType has store {
    Transferable(NFT),
    Soulbound(SoulboundToken),
    Restricted(RestrictedAsset),
}

Однак це вимагає вичерпного узгодження і погано масштабується між модулями.

Отже, знову:обгортання + динамічних полів + тип метаданиє більш гнучким.


3. Як підтримувати безпеку типу при отриманні?

Переміщення виконуєстирання типув динамічних полях. Отже, як тільки ви отримуєте значення з динамічного поля, ви просто отримуєте значення певного типу V- але ви не знаєте, що Vвідбувається під час виконання.

Щоб вирішити це безпечно, потрібно:

  • Зберігайтеметадані типупоряд із значенням.
  • Використовуйтешаблон свідкаабовбудовування тегів типудля повторного підтвердження інформації про тип після пошуку.

Шаблон: Зберігати метадані типу

struct AssetWithMetadata<T: key> has store {
    value: T,
    type_info: TypeInfo,
}

Потім зберігайте AssetWithMetadata<NFT>або AssetWithMetadata<SoulboundToken>в динамічному полі.

Коли ви отримуєте, перевірте type_infoочікуваний тип, перш ніж робити щось небезпечне.


4. Візерунок свідків та типи фантомів

Ви можете абсолютно використовувати фантомні типи як свідків для кодування поведінки, наприклад, передачі.

Наприклад:

struct Transferable; // marker type
struct NonTransferable; // marker type

struct Asset<T: drop + copy + store + key, Policy: phantom> has key, store {
    id: UID,
    policy: PhantomData<Policy>,
}

Потім:

type NFT = Asset<_, Transferable>;
type Soulbound = Asset<_, NonTransferable>;

Потім ви можете написати логіку, як:

public fun transfer<T>(asset: Asset<T, Transferable>) {
    // allowed
}

public fun transfer<T>(_asset: Asset<T, NonTransferable>) {
    // disallowed
    abort();
}

Це дає змогу виконувати правила передачі під час компіляції.

Але для зберігання обох типів в одній колекції вам все одно знадобиться обгортка:

struct AnyAsset has store {
    data: Vector<u8>,
    type_info: TypeInfo,
}

type_infoІ обробляти десеріалізацію динамічно на основі.

5
Коментарі
.
0xduckmove.
Jun 18 2025, 04:11

Ось як ви можете створити ринок Sui Move, який підтримує як передавальні (NFT, ключі+магазин), так і прив'язані до душі (лише ключові) активи, зберігаючи при цьому обмеження безпеки типу та можливостей обробки.

Вимоги до можливостей для динамічних полів

Так, V повинен мати сховище під час компіляції для dynamic_field: :add<K, V> () та подібних колекцій.

  • сховище потрібно для того, щоб будь-яке значення зберігалося в глобальному сховищі, включаючи динамічні поля та таблиці.
  • Типи обгортки не можуть обійти це: якщо ви загортаєте тип без зберігання у структурі, обгортка також не може мати сховище, оскільки всі поля повинні мати сховище, щоб структура мала сховище (source).

Гетерогенне зберігання в мішку

  • Сумка (або будь-яка динамічна польова колекція) може зберігати лише типи, які задовольняють необхідні здібності.
  • Для динамічних полів об'єкта значення має мати ключ + сховище.
  • Для динамічних полів значення має мати сховище.
  • Ви не можете зберігати тип із лише ключем (наприклад, токен, пов'язаний із душею) у сумці, який потребує зберігання.
  • Це фундаментальне обмеження системи типу Move (source).

Тип безпеки та стирання типу

  • Динамічні поля стираються під час виконання. Ви повинні знати тип, який потрібно отримати під час доступу.
  • Візерунок для безпеки типу:
  • Зберігати метадані типу (наприклад, enum або typeName) поряд із значенням.
  • Використовуйте структуру обгортки, яка включає як актив, так і інформацію про його тип.
  • Під час отримання перевірте метадані перед кастингом.
  • Приклад візерунка:
  • Зберігати список {asset_type: u8,...} і використовуйте asset_type, щоб визначити, як обробляти актив.

Шаблон свідків та типи фантомів

  • Фантомні типи та шаблон свідків допомагають під час компіляції, а не під час виконання.
  • Ви можете використовувати активи для різних типів, але вам все одно потрібно переконатися, що всі Т мають необхідні здібності для колекції.
  • Під час виконання ви не можете витягти інформацію про тип з фантомного типу; ви повинні зберігати явні метадані (source).
  1. Дизайн ринку для змішаних типів активів
  • Передавальні активи (NFT): Використовуйте ключ+магазин, зберігайте в динамічних полях об'єкта або Сумки.

  • Токени Soulbound (тільки ключі): Не можна зберігати в динамічних полях або сумках, які потребують зберігання. Ви повинні обробляти їх окремо, наприклад, зберігаючи лише метадані або посилання (не сам об'єкт).

  • Користувальницькі активи з обмеженнями: Поки вони мають ключ+магазин, ви можете зберігати їх. Якщо ні, вам потрібен окремий шлях обробки.

  • Всі значення в динамічних полях або мішках повинні мати сховище (і ключ для динамічних полів об'єкта).

  • Ви не можете зберігати типи з лише ключем у цих колекціях.

  • Безпека типу під час виконання вимагає явних метаданих.

  • Фантомні типи/шаблон свідків допомагають під час компіляції, а не під час виконання.

2
Коментарі
.
md rifat hossen.
Jun 19 2025, 17:11

Дякую Xavier.eth за це продумане та технічне запитання. Ось мій внесок щодо обмежень здібностей та динамічних полів у Sui Move:

Так, будь-яке значення (V), яке використовується в, dynamic_field::add<K, V>()повинно мати storeздатність. Це суворо дотримується під час перевірки під час компіляції Move. Навіть обгортання немагазинного значення всередині структури не допоможе — якщо не вистачає якогось поляstore, вся структура також втрачає його.

key + storeЗберігання неоднорідних активів з різними здібностями можливо, але лише за умови обмеження, що всі активи в динамічному полі/мішках повинні задовольняти один і той же набір здібнок(як правило).

🔒Активи, пов'язані з душею, з єди keyною можливістю не можуть зберігатися безпосередньов таких колекціях. Замість цього можна:

  • Зберігайтетільки метадани(ID, тип enum тощо)
  • Використовуйте паралельне відображення, як dynamic_field::add<AssetID, Metadata>для душевних типів.
  • Ведіть реєстр на основі посилань, який відстежує списки ресурсів, пов'язаних із душею, не зберігаючи повний об'єкт.

🧩Фантомні типидопомагаютьлише під час компіляції. Вам все одно знадобляться явні метадані типу виконання для фактичної логіки пошуку.

🚀Запропонована гібридна архітектура:

  • Bag<AssetID, TransferableAsset>для активів з store
  • Map<AssetID, SoulboundMetadata>для типів тільки ключів
  • AssetWrapperструктура з type_idабо u8 asset_typeдля ідентифікації типу під час виконання

Ще раз спасибі! Я з нетерпінням чекаю розвитку цього ринку!

— мд Ріфат Хоссен

2
Коментарі
.
24p30p.
24p30p2042
Jul 9 2025, 05:17

Якщо ви створюєте ринок, який підтримує кілька типів активів з різними здібностями в Sui Move, ключовим завданням є те, якобмеження здатностіMove взаємодіють іздинамічними полямита неоднорідними даними. Ви не можете безпосередньо зберігати значення без storeможливості, тому так — коли ви використовуєтеdynamic_field::add<K, V>(), значення Vмає мати storeможливість під час компіляції. storeЦе означає, що ви не можете безпосередньо зберігати щось на зразок токена, пов'язаного з душею, якщо він не загорнутий у тип, який має. Щоб обійти це, ви можете використатиструктури обгортки, які містять метадані про ресурс або ідентифікатор посилання, задовольняючи необхідні здібності. Наприклад, SoulboundListing<T: key>обгортка може містити лише посилання або метадані, а не повний об'єкт. Для гетерогенного сховища a Bagабо Tableможе зберігати лише один тип для кожної інстанціації, але ви можете змусити його працювати, обгорнувши ресурси у спеціальний тип, подібний до енуму, або використовуючи фантомні типи, схожі на ознаки, для імітації інформації про тип. Ви не отримаєте нативного поліморфізму, але ви можете відстежувати типи ресурсів за допомогою ручного тегування або використовуватишаблон свідка, де фантомні типи та дискримінанти виконання допомагають імітувати поділ на рівні типу. Якщо ви використовуєте Asset<T>для кожного типу ресурсу, T1і обидва вони є ф T2антомними, тоді так — ви можете зберігати Asset<T1>та Asset<T2>у спільному контейнері, стираючи їх у загальній обгортці. Потім вам потрібно буде зберігати інформацію про тип окремо (наприклад, type_idполе), щоб правильно відповідати під час читання. Це баланс міжбезпекою типу,ефективністю газутаскладністю проектування. Використання окремих колекцій для кожного типу ресурсу є найпростішим для безпеки, але типи обгортки та індексовані метадані є більш масштабованими, коли вам потрібна гнучкість. Вам потрібно буде вибирати, виходячи з того, чи важливіше вартість газу чи простота розробника для вашого випадку використання.

1
Коментарі
.

Ви знаєте відповідь?

Будь ласка, увійдіть та поділіться нею.

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

621Пости1665Відповіді
Sui.X.Peera.

Зароби свою частку з 1000 Sui

Заробляй бали репутації та отримуй винагороди за допомогу в розвитку спільноти Sui.

Кампанія винагородСерпень