Beitrag
Teile dein Wissen.
+15
Wie interagieren Fähigkeitsbeschränkungen mit dynamischen Feldern in heterogenen Sammlungen?
Ich baue einen Marktplatz auf, der mit mehreren Asset-Typen mit unterschiedlichen Fähigkeitsanforderungen umgehen muss, und ich bin auf einige grundlegende Fragen zum Typensystem von Move gestoßen. Ich möchte verschiedene Asset-Typen in derselben Sammlung speichern, aber sie haben unterschiedliche Fähigkeiten:
- Reguläre NFTs:
key + store
(übertragbar) key
Nur Soulbound-Token (nicht übertragbar)- Benutzerdefinierte Vermögenswerte mit Übertragungsbeschränkungen
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? */ }
Die wichtigsten Fragen:
- Anforderungen an die Fähigkeiten: Wird bei der Verwendung
dynamic_field::add<K, V>()``V
immerstore
zur Kompilierzeit benötigt? Können Wrapper-Typen das umgehen? - Heterogener Speicher: Kann ein einziger Beutel Objekte mit unterschiedlichen Fähigkeiten (
key + store + copy
vskey + store
) speichern und sie zur Laufzeit unterschiedlich handhaben? - Typsicherheit: Da dynamische Felder eine Typlöschung durchführen, wie kann ich die Typsicherheit beim Abrufen von Werten gewährleisten? Nach welchem Muster werden Typmetadaten gespeichert?
- Zeugenmuster: Wie funktionieren Fähigkeitsbeschränkungen bei Phantomtypen? Kann ich
Asset<Type1>``Asset<Type2>
Informationen zum Typ speichern und in derselben Sammlung speichern und zu einem späteren Zeitpunkt extrahieren?
Aufbau eines Systems, in dem NFTs, Soulbound-Token und eingeschränkte Vermögenswerte alle Marktplatzfunktionen benötigen, jedoch mit unterschiedlicher Übertragungssemantik.
Ich habe Wrapper-Typen ausprobiert, mehrere Sammlungen pro Fähigkeitssatz, separate Typ-Metadatenspeicherung. Jede hat Kompromisse zwischen Typsicherheit, Gaskosten und Komplexität.
- Sui
- Architecture
Antworten
5Sie stoßen auf den Kern des Typ- und Fähigkeitssystems von Move und Sie haben absolut Recht, wenn Sie diese Fragen bei der Gestaltung eines heterogenen Marktplatzes aufwerfen.
Zunächst zu den Fähigkeitsbeschränkungen: Jeder Wert, den Sie mit dynamic_field: :add<K, V> () speichern, muss die Fähigkeit haben, zur Kompilierzeit zu speichern. Dies ist eine harte Regel in Move: Werte ohne Speicher können einfach nicht im Speicher gespeichert werden, egal ob in dynamischen Feldern, Tabellen oder ähnlichen Konstrukten. Der Versuch, einen Nichtspeichertyp in eine Struktur einzuschließen, funktioniert auch nicht — Move ist hier strikt. Wenn auch nur ein Feld keinen Speicherplatz hat, verliert auch der gesamte Wrapper an Speicherplatz. Leider gibt es hier keinen Wrapper-Workaround. Soulbound-Assets, die nur einen Schlüssel (und keinen Speicher) enthalten, sind von jeder Sammlung ausgeschlossen, die in einen globalen Speicher schreibt, wie z. B. ein Bag oder ein dynamisches Feld. Dies ist in der Dokumentation zu den Move-Fähigkeiten gut dokumentiert.
Was das Speichern verschiedener Asset-Typen in derselben Sammlung angeht: Wenn sie alle Key + Store implementieren, dann ja, ein einziger Bag kann sie speichern, aber unter der Haube macht Move keinen echten Polymorphismus. Der Typ muss zum Zeitpunkt des Zugriffs bekannt sein, und der Bag muss aus Sicht des Compilers homogen sein. Dies führt zu Ihrer nächsten Frage: Wie gewährleisten wir die Typsicherheit in dynamischen Feldern? Da Move die Typen für diese Sammlungen löscht, merkt es sich nicht, welcher genaue Typ darin enthalten ist — Sie müssen die Zuordnung angeben. Ein gängiges Muster besteht darin, jedes Asset in eine Struktur einzuschließen, die Metadaten wie eine u8-Aufzählung oder einen TypeName enthält. Wenn Sie das Objekt abrufen, überprüfen Sie die Metadaten und stufen sie entsprechend herunter oder behandeln sie entsprechend. Es gibt kein magisches RTTI; Sie täuschen es mit Datendisziplin vor.
Nun zu Phantomtypen und Zeugenmustern — diese sind hilfreich für die Sicherheit bei der Kompilierung, bieten Ihnen aber keinen Einblick in die Laufzeit.
In Ihrem Marktplatz-Design bedeutet das, dass Sie wahrscheinlich nach einer hybriden Strategie suchen. Für reguläre NFTs und andere übertragbare Assets kannst du dynamische Objektfelder oder Bags frei verwenden, solange sie dem Key+Store entsprechen. Für soulbound-Assets, bei denen es sich nur um wichtige Assets handelt, ist es am besten, Referenzen und zugehörige Metadaten in einer parallelen Metadatentasche zu speichern oder einen benutzerdefinierten Auflistungstyp zu verwenden, der das Asset selbst nicht speichert, sondern dessen Existenz über ID und ergänzende Daten aufzeichnet. Es ist komplexer, bietet Ihnen aber die Flexibilität, die Übertragungssemantik ordnungsgemäß durchzusetzen.
Referenzen
1. store
Müssen Werte in dynamischen Feldern immer benötigt werden?
dynamic_field::add
Wenn Sie den Wert direkt in einem dynamischen Feld (z. B. viastore
)speichernmöchten, muss er alle für den Feldtyp erforderlichen Fähigkeiten erfüllen — einschließlich. Wenn V
dies nicht der Fall iststore
, können Sie ihn nicht direkt in ein dynamisches Feld einfügen.
Aber hier ist der Trick:
Siekönnensolche Typen in eine andere Struktur einschließen, die das hat
store
, auch wenn der innere Typ das nicht tut.
Beispiel:
struct AssetWrapper<T: key> has store {
inner: T,
}
Auch wenn es T
nur hatkey
, AssetWrapper<T>
kann gespeichert werden, weil es selbst hatstore
.
Jetzt kannst du Folgendes tun:
dynamic_field::add(&mut object, key, AssetWrapper { inner: soulbound_token });
Auf diese Weise können Sie sowohl übertragbare als auch nicht übertragbare Vermögenswerte unter derselben Abstraktion speichern.
#2 Kann eine einzelne Tasche Gegenstände mit unterschiedlichen Fähigkeiten verstauen?
Die kurze Antwort lautet:ja, wobei Wrapper und Tipplöschtechniken verwendet werden. Aber es gibt Kompromisse.
In Move können Sie nicht direkt eine Liste mit mehreren konkreten Typen haben, es sei denn, sie sind in einen generischen Container verpackt.
Ein guter Ansatz ist die Verwendung eines Wrappers wie:
struct AnyAsset has store {
// Maybe include metadata or witness type info
data: Vector<u8>, // serialized representation?
type_info: TypeInfo, // Type metadata
}
Oder noch besser, verwende eine Enum-Kodierung im Variantenstil (sofern dies von deinem Move-Dialekt unterstützt wird):
enum AssetType has store {
Transferable(NFT),
Soulbound(SoulboundToken),
Restricted(RestrictedAsset),
}
Dies erfordert jedoch ein umfassendes Matching und lässt sich nicht gut zwischen den Modulen skalieren.
Also nochmal:Wrapping + dynamische Felder + Typ Metadatenist flexibler.
#3. Wie kann die Typsicherheit beim Abrufen gewahrt werden?
Move führt in dynamischen FeldernTyplöschungdurch. Sobald Sie also einen Wert aus einem dynamischen Feld abrufen, erhalten Sie nur einen Wert irgendeines Typs V
— aber Sie wissen nicht, was zur Laufzeit der Wert V
ist.
Um dies sicher zu lösen, müssen Sie:
- Speichern Sie neben dem WertTyp-Metadaten.
- Verwenden Sie einZeugenmusteroderTyp-Tag-Einbettung, um Typinformationen beim Abrufen wieder einzufügen.
Muster: Typ-Metadaten speichern
struct AssetWithMetadata<T: key> has store {
value: T,
type_info: TypeInfo,
}
Dann speichern AssetWithMetadata<NFT>
oder AssetWithMetadata<SoulboundToken>
in einem dynamischen Feld.
Überprüfen Sie beim Abrufen type_info
den erwarteten Typ, bevor Sie etwas Unsicheres tun.
#4 Zeugenmuster und Phantomtypen
Sie können auf jeden Fall Phantomtypen als Zeugen verwenden, um Verhalten wie Übertragbarkeit zu kodieren.
Zum Beispiel:
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>,
}
Dann:
type NFT = Asset<_, Transferable>;
type Soulbound = Asset<_, NonTransferable>;
Sie können dann Logik schreiben wie:
public fun transfer<T>(asset: Asset<T, Transferable>) {
// allowed
}
public fun transfer<T>(_asset: Asset<T, NonTransferable>) {
// disallowed
abort();
}
Auf diese Weise können Sie Übertragungsregeln während der Kompilierung durchsetzen.
Aber um beide Typen in derselben Sammlung zu speichern, brauchst du immer noch einen Wrapper:
struct AnyAsset has store {
data: Vector<u8>,
type_info: TypeInfo,
}
type_info
Und behandeln Sie die Deserialisierung dynamisch auf der Grundlage von.
So können Sie einen Sui Move-Marktplatz entwerfen, der sowohl übertragbare (NFTs, Schlüssel + Store) als auch seelengebundene (nur Schlüssel) Assets unterstützt und gleichzeitig die Typsicherheit und die Einschränkungen der Bedienbarkeit beibehält.
Anforderungen an die Fähigkeiten dynamischer Felder
Ja, V muss zur Kompilierzeit einen Speicher für dynamic_field: :add<K, V> () und ähnliche Sammlungen haben.
- Store ist für jeden Wert erforderlich, der im globalen Speicher gespeichert werden soll, einschließlich dynamischer Felder und Tabellen.
- Wrapper-Typen können das nicht umgehen: Wenn Sie einen Typ ohne Speicher in eine Struktur einschließen, kann der Wrapper auch keinen Speicher haben, da alle Felder einen Speicher haben müssen, damit die Struktur einen Speicher hat (source).
Heterogener Speicher in einer Tasche
- In einer Tasche (oder einer anderen dynamischen, feldbasierten Sammlung) können nur Typen aufbewahrt werden, die die erforderlichen Fähigkeiten erfüllen.
- Bei dynamischen Objektfeldern muss für den Wert der Schlüssel + gespeichert sein.
- Für dynamische Felder muss der Wert gespeichert sein.
- Du kannst einen Typ, der nur einen Schlüssel enthält (z. B. ein Soulbound-Token), nicht in einer Tasche speichern, die gespeichert werden muss.
- Dies ist eine grundlegende Einschränkung des Move-Typsystems (source).
Tippsicherheit und Typlöschung
- Dynamische Felder werden zur Laufzeit typgelöscht. Sie müssen den Typ kennen, den Sie beim Zugriff abrufen möchten.
- Muster für die Typsicherheit:
- Speichern Sie Typmetadaten (z. B. eine Aufzählung oder TypeName) neben dem Wert.
- Verwenden Sie eine Wrapper-Struktur, die sowohl das Asset als auch dessen Typinformationen enthält.
- Überprüfen Sie beim Abrufen die Metadaten vor dem Casting.
- Beispielmuster:
- Speichern Sie Listing {asset_type: u8,...} und verwenden Sie asset_type, um zu bestimmen, wie mit dem Asset umgegangen werden soll.
Zeugenmuster und Phantomtypen
- Phantomtypen und das Witness-Pattern helfen bei der Kompilierung, nicht bei der Laufzeit.
- Sie können Asset
für verschiedene Typen verwenden, müssen aber trotzdem sicherstellen, dass alle T über die erforderlichen Fähigkeiten für die Sammlung verfügen. - Zur Laufzeit können Sie keine Typinformationen aus einem Phantomtyp extrahieren; Sie müssen explizite Metadaten speichern (source).
- Marktplatz-Design für gemischte Anlagetypen
-
Übertragbare Vermögenswerte (NFTs): Verwenden Sie Schlüssel und speichern Sie sie in dynamischen Objektfeldern oder Taschen.
-
Soulbound-Token (nur Schlüssel): Können nicht in dynamischen Feldern oder Taschen gespeichert werden, die aufbewahrt werden müssen. Du musst sie separat behandeln, z. B. indem du nur Metadaten oder Referenzen speicherst (nicht das Objekt selbst).
-
Benutzerdefinierte Assets mit Einschränkungen: Solange für sie der Schlüssel + gespeichert ist, können Sie sie speichern. Wenn nicht, benötigen Sie einen separaten Behandlungspfad.
-
Alle Werte in dynamischen Feldern oder Bags müssen einen Speicher (und einen Schlüssel für dynamische Objektfelder) haben.
-
In diesen Sammlungen können Sie keine Typen speichern, die nur Schlüssel enthalten.
-
Für die Typsicherheit zur Laufzeit sind explizite Metadaten erforderlich.
-
Hilfe bei Phantomtypen/Zeugenmustern zur Kompilierzeit, nicht zur Laufzeit.
Vielen Dank Xavier.eth für diese durchdachte und technische Frage. Hier ist mein Beitrag zu Fähigkeitsbeschränkungen und dynamischen Feldern in Sui Move:
V
✅Ja, jeder Wert (dynamic_field::add<K, V>()
), der in verwendet wird, store
muss eine Fähigkeit haben.
Dies wird bei den Kompilierzeitprüfungen von Move strikt durchgesetzt. Selbst das Einschließen eines Werts, der nicht gespeichert ist, in eine Struktur hilft nicht — wenn ein Feld fehltstore
, verliert es auch die gesamte Struktur.
key + store
✅Das Speichern heterogener Vermögenswerte mit unterschiedlichen Fähigkeiten ist möglich, aber nur unter der Bedingung, dass alle Vermögenswerte im dynamischen Feld/den dynamischen Taschen den gleichen Fähigkeitssatz erfüllen müssen(in der Regel).
🔒Seelengebundene Vermögenswerte, die nur key
Fähigkeiten besitzen, können nicht direkt in solchen Sammlungen gespeichert werden. Stattdessen kannst du:
- Speicherenur Metadaten(ID, Typ enum usw.)
dynamic_field::add<AssetID, Metadata>
- Benutze ein paralleles Mapping wie bei seelengebundenen Typen. - Führe eine referenzbasierte Registry, die Soulbound-Asset-Listings nachverfolgt, ohne das gesamte Objekt zu speichern.
🧩Phantomtypenhelfennur bei der Kompilierzeit. Für die eigentliche Abruflogik benötigen Sie weiterhin explizite Metadaten vom Runtime-Typ.
🚀Vorgeschlagene Hybridarchitektur:
Bag<AssetID, TransferableAsset>
- für Vermögenswerte mit store
Map<AssetID, SoulboundMetadata>
für Typen, die nur Schlüssel enthaltenAssetWrapper
Struktur mittype_id
oderu8 asset_type
zur Typidentifizierung zur Laufzeit
Nochmals vielen Dank! Ich freue mich darauf, zu sehen, wie sich dieser Marktplatz weiterentwickelt!
— Herr Rifat Hossen
Wenn Sie einen Marktplatz aufbauen, der mehrere Asset-Typen mit unterschiedlichen Fähigkeiten in Sui Move unterstützt, besteht die größte Herausforderung darin, wie dieFähigkeitseinschränkungenvon Move mitdynamischen Feldernund heterogenen Daten interagieren. store``dynamic_field::add<K, V>()``V
Ohne die Fähigkeit können Sie Werte nicht direkt speichernstore
, also ja — wenn Sie sie verwenden, muss der Wert bei der Kompilierung über die Fähigkeit verfügen. store
Das bedeutet, dass Sie so etwas wie ein Soulbound-Token nicht direkt speichern können, es sei denn, es ist in einen Typ eingebunden, der es hat. Um dies zu umgehen, können SieWrapper-Strukturenverwenden, die Metadaten über das Asset oder eine Referenz-ID enthalten und gleichzeitig die erforderlichen Fähigkeiten erfüllen. Ein SoulboundListing<T: key>
Wrapper kann beispielsweise nur eine Referenz oder Metadaten enthalten, nicht das vollständige Objekt. Bei heterogenem Speicher Bag``Table
kann ein oder nur einen Typ pro Instanziierung speichern, aber Sie können dafür sorgen, dass dies funktioniert, indem Sie Assets in einen aufzählungsähnlichen benutzerdefinierten Typ einschließen oder merkmalsähnliche Phantomtypen verwenden, um Typinformationen zu simulieren. Sie erhalten zwar keinen systemeigenen Polymorphismus, aber Sie können Asset-Typen mit manuellem Tagging verfolgen oder dasWitness-Musterverwenden, bei dem Sie mithilfe von Phantomtypen und Laufzeitdiskriminanten die Trennung auf Typebene simulieren können. Asset<T>``T1
Wenn Sie T2
für jeden Asset-Typ verwenden und beide ein Asset<T1>``Asset<T2>
Phantom sind, dann ja — Sie können und in einem gemeinsam genutzten Container speichern, indem Sie den Typ in einem gemeinsamen Wrapper löschen. Sie müssen die Typinformationen dann separat speichern (wie ein type_id
Feld), damit sie beim Lesen korrekt zugeordnet werden. Es ist ein Balanceakt zwischenTypsicherheit,GaseffizienzundKonstruktionskomplexität. Die Verwendung separater Sammlungen pro Asset-Typ ist aus Sicherheitsgründen am saubersten, aber Wrapper-Typen und indizierte Metadaten sind besser skalierbar, wenn Sie Flexibilität wünschen. Sie müssen je nachdem, ob die Gaskosten oder die Einfachheit des Entwicklers für Ihren Anwendungsfall wichtiger sind, eine Auswahl treffen.
Weißt du die Antwort?
Bitte melde dich an und teile sie.
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Verdiene deinen Anteil an 1000 Sui
Sammle Reputationspunkte und erhalte Belohnungen für deine Hilfe beim Wachstum der Sui-Community.
- Warum benötigt BCS eine genaue Feldreihenfolge für die Deserialisierung, wenn Move-Strukturen benannte Felder haben?53
- Fehler bei der Überprüfung mehrerer Quellen“ in den Veröffentlichungen des Sui Move-Moduls — Automatisierte Fehlerbehebung43
- Sui-Transaktion schlägt fehl: Objekte sind für eine andere Transaktion reserviert25
- Wie interagieren Fähigkeitsbeschränkungen mit dynamischen Feldern in heterogenen Sammlungen?05