Publication
Partagez vos connaissances.
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
transfer : :public_receive
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'
**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.
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
Pourquoi public_receive au lieu de simplement appeler receive ? Si nous essayions transfer : :receive
**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