Sui.

Publication

Partagez vos connaissances.

article banner.
harry phan.
Apr 24, 2025
Article

Gestion des enfants multimodule avec public_receive

Il s'agit de la troisième partie de la série « Les objets parent-enfant dans Sui Move ».

Parfois, vos types de parents et d'enfants sont définis dans différents modules ou même dans différents packages. Par exemple, vous pouvez disposer d'un objet Warehouse générique qui peut stocker n'importe quel type d'objets Parcel. Le module Warehouse souhaite extraire un enfant de colis, mais le type de colis est défini ailleurs. Dans de tels cas, nous utilisons transfer : :public_receive, qui est le cousin intermodule de receive.

###recevoir contre public_receve

Comme nous l'avons vu, transfer : :receive ne peut être appelé que dans le module qui définit T (ou un ami) car il ne nécessite pas T : store. Le vérificateur de bytecode Move garantit en fait que dans tout appel à recevoir, le type T provient du module en cours. Il s'agit d'une restriction de sécurité pour les objets dotés uniquement d'une clé.

transfer : :public_receive est une variante qui requierce T : key + store mais permet de recevoir en dehors du module de T. En d'autres termes, si le type d'objet possède la capacité de stockage (ce qui signifie qu'il est autorisé à exister librement dans le stockage global), alors n'importe quel module (avec l'UID &mut du parent) peut le recevoir en utilisant public_receive. C'est parfait pour les cas où le module du parent est différent de celui de l'enfant.

Pourquoi avoir besoin d'un magasin ? Parce que stockez les marques indiquant que l'objet peut être conservé en toute sécurité et transmis en dehors de son module de définition. Les objets à clé uniquement peuvent avoir des invariants personnalisés que le module d'origine souhaite appliquer lors du transfert/de la réception ; en les excluant de public_receive, Sui oblige les développeurs à les gérer dans le module (comme nous le verrons avec les objets liés à l'âme). Si un objet a un stockage, il est plus permissif, et Sui autorise une logique générique de transfert/réception pour le gérer en externe.

###Exemple : modules séparés pour les parents et les enfants

Illustrons avec un scénario simple : un entrepôt qui stocke des objets Parcel. Le type de colis est défini dans son propre module et l'entrepôt dans un autre. Nous allons montrer comment Warehouse peut recevoir un colis enfant à l'aide de public_receive.

module demo::parcel {  // Child module
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};

    /// A parcel object that can be stored in a Warehouse.
    /// It has both key and store, so it can be transferred across modules.
    struct Parcel has key, store { id: UID, contents: vector<u8> }

    public entry fun create_parcel(contents: vector<u8>, ctx: &mut TxContext): Parcel {
        Parcel { id: object::new(ctx), contents }
    }
}
module demo::warehouse {  // Parent module
    use sui::transfer::{Self, Receiving, public_receive};
    use demo::parcel::{Self, Parcel};
    use sui::object::{UID};
    use sui::tx_context::{Self, TxContext};

    struct Warehouse has key { id: UID, location: address }

    public entry fun create_warehouse(location: address, ctx: &mut TxContext): Warehouse {
        Warehouse { id: object::new(ctx), location }
    }

    /// Receive a Parcel that was sent to this Warehouse. 
    /// Returns the Parcel to the caller (transferred to caller's address).
    public entry fun withdraw_parcel(
        warehouse: &mut Warehouse, 
        parcel_ticket: Receiving<Parcel>, 
        ctx: &mut TxContext
    ): Parcel {
        // Using public_receive because Parcel is defined in another module and has store
        let parcel = public_receive<Parcel>(&mut warehouse.id, parcel_ticket) [oai_citation_attribution:27‡docs.sui.io](https://docs.sui.io/concepts/transfers/transfer-to-object#:~:text=%2F%2F%2F%20Given%20mutable%20,argument) [oai_citation_attribution:28‡github.com](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/transfer.move#:~:text=public%20fun%20public_receive,T%3E%29%3A%20T);
        // Transfer the parcel to the transaction sender (so the caller gets ownership)
        transfer::transfer(parcel, tx_context::sender(ctx));
        // We return nothing because we've transferred the Parcel out to the caller.
    }
}

Découvrons ce qui se passe dans withdraw_parcel :

  • Nous appelons public_receive (&mut warehouse.id, parcel_ticket). Comme Parcel a la capacité de stocker, cet appel est autorisé même si nous ne sommes pas dans le module Colis. En principe, cela effectue les mêmes vérifications et extractions que la réception, mais cela est autorisé entre les modules car store indique que cela peut être fait en toute sécurité. https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/transfer.move#:~:text=public%20fun%20public_receive,T%3E%29%3A%20T
  • Nous transférons ensuite immédiatement le colis reçu à l'adresse de l'appelant (tx_context : :sender (ctx)). Cette étape garantit que le colis quitte l'entrepôt et est envoyé à l'utilisateur qui a initié le retrait. Nous aurions également pu simplement renvoyer Parcel à partir de la fonction, et Sui le traiterait comme une sortie appartenant à l'adresse de l'appelant (puisqu'il s'agit d'une sortie de fonction d'entrée). Faire un transfert explicite est plus détaillé, mais cela permet de comprendre ce qui se passe (et nous permet de faire toutes les vérifications avant de libérer l'objet).

Pourquoi inclure le point de vente dans le colis ? Si Parcel n'avait pas la capacité de stockage (c'est-à-dire s'il n'avait que la clé), l'appel public_receive dans demo : :warehouse ne serait pas compilé — Sui impose que T possède un store pour public_receive. Dans ce cas, nous serions obligés de récupérer le colis en utilisant receive dans le module de colis lui-même (ou en utilisant une relation d'amitié), ce qui complique la conception intermodule. En ajoutant store à Parcel, nous disons en fait que « cet objet peut être déplacé et reçu librement par des modules externes », ce que nous voulons pour un modèle de conteneur générique.

**Modèle d'appel de fonction :**Pour les utiliser dans une transaction, le flux serait le suivant :

1.**Dépôt (transfert vers un objet) :**Appelez transfer : :public_transfer (parcel_obj, @warehouse_id) pour envoyer un colis dans un entrepôt. Cela indique que le propriétaire du colis est l'entrepôt. (Nous utilisons public_transfer ici car il se trouve en dehors du module Parcel et Parcel a une boutique. Dans le module du colis, un simple transfert fonctionnerait également.) 2. **Retirer (recevoir un retour) :**Plus tard, appelez withdraw_parcel (warehouse_obj, Receiving (parcel_id,...)). La réception peut être obtenue par le SDK en faisant référence à l'identifiant du colis et à la dernière version. La fonction appellera public_receive puis vous transférera le colis.

Après l'appel withdraw_parcel, le propriétaire du colis revient à une adresse (la vôtre). Il s'agit donc à nouveau d'un objet appartenant à une adresse normale. L'entrepôt n'en est plus propriétaire.

**Considérations relatives à l'intermodule :**Notez que le module Warehouse devait connaître le type de colis (nous utilisons demo : :parcel : :Parcel). En effet, nous saisissons explicitement la réception comme réception. Si vous vouliez un conteneur véritablement générique pouvant recevoir n'importe quel type d'objet, vous devrez utiliser des génériques ou une approche différente (éventuellement des champs dynamiques avec effacement de type). Mais dans la plupart des cas d'utilisation, vous saurez à quels types d'enfants vous attendez.

Pourquoi public_receive au lieu de simplement appeler receive ? Si nous essayions transfer : :receive (&mut warehouse.id, parcel_ticket) dans le module warehouse, le vérificateur Move le rejetterait car Parcel n'est pas défini dans demo : :warehouse. Sui fournit le public_receive comme moyen privilégié de le faire avec une vérification de capacité supplémentaire (nécessitant un magasin). De même, Sui a transfer vs public_transfer, freeze_object vs public_freeze_object, etc., suivant le même schéma : les versions public_ sont destinées à être utilisées en dehors du module de définition et nécessitent un stockage.

**N' oubliez pas l'autorisation des parents :**Même avec public_receive, vous avez toujours besoin de &mut warehouse.id. Nous l'avons obtenu car withdraw_parcel se trouve dans le module de Warehouse et accepte &mut Warehouse. Ainsi, seule une personne capable de l'appeler (le propriétaire de l'entrepôt) peut retirer le colis. Si le module d'entrepôt ne fournissait pas une telle fonction publiquement, personne ne pourrait non plus appeler public_receive en externe sur ses enfants. Ainsi, le cross-module ne contourne pas le contrôle du parent ; il permet simplement au code du parent de fonctionner avec des enfants de types qu'il n'a pas définis.

Remarque sur la capacité de stockage : Le fait de donner un magasin d'objets le rend plus flexible mais légèrement moins restreint. Tout module avec la référence parent peut l'extraire à l'aide de public_receive. Si vous souhaitezrestreindrela manière dont un objet est récupéré (par exemple, appliquer une logique personnalisée ou empêcher une extraction facile), vous pouvez délibérément en faire une clé uniquement. Nous en verrons un exemple avec les objets liés à l'âme. Dans ces cas, vous pouvez implémenter une fonction de réception personnalisée au lieu de vous fier à public_receive.

Pour résumer cette partie : public_receive est votre ami pour la gestion des objets enfants définis dans d'autres modules, à condition que ces objets puissent être stockés. Il vous permet de créer des systèmes multimodules (comme notre Entrepôt/Colis) tout en respectant la propriété et le contrôle d'accès. N'oubliez pas d'inclure le store sur les types d'enfants et d'utiliser public_transfer lorsque vous les envoyez à un parent depuis l'extérieur de son module.

  • Sui
  • Architecture
5
Partager
Commentaires
.

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

259Publications368Réponses
Sui.X.Peera.

Gagne ta part de 1000 Sui

Gagne des points de réputation et obtiens des récompenses pour avoir aidé la communauté Sui à se développer.

Campagne de RécompensesMai
      Nous utilisons des cookies pour vous assurer la meilleure expérience sur notre site Web.
      Plus d'infos