Traduction et relecture : "Communauté chinoise Starknet"
aperçu
La version 2 du compilateur Cairo introduit des changements dans la syntaxe Starknet pour rendre le code plus explicite et sûr. L'interface publique du contrat intelligent est définie à l'aide de traits, et l'accès au stockage se fait via le trait ContractState. Les méthodes privées doivent être définies avec une implémentation différente de l'interface publique. Les événements sont désormais définis comme des énumérations, où chaque variante est une structure du même nom.
Avis de non-responsabilité : les termes utilisés ici font référence à différentes versions du compilateur Cairo, et la syntaxe est provisoire car la communauté Starknet débat toujours du meilleur terme à utiliser. Une fois déterminé, cet article sera mis à jour en conséquence.
Le compilateur v2
La semaine dernière, une nouvelle version majeure 2.0.0-rc0 du compilateur Cairo a été publiée sur Github. Le nouveau compilateur apporte des améliorations significatives au plugin Starknet, rendant notre code plus sûr, plus explicite et plus réutilisable. Notez que cette nouvelle version du compilateur n'est pas encore prise en charge sur Starknet testnet ou mainnet car elle est toujours en cours d'élaboration dans l'environnement intégré.
Le but de cet article est de vous montrer comment réécrire un contrat intelligent Starknet créé pour la version 1.x du compilateur Cairo en un contrat intelligent compatible avec la version 2.x du compilateur. Notre point de départ est le contrat intelligent Ownable créé dans l'article précédent, qui est compatible avec la version 1.x du compilateur Cario.
[contract] mod Ownable {use starknet::ContractAddress;use starknet::get_caller_address;
fn only_owner() {let caller = get_caller_address();assert(caller == owner::read(), 'Caller is not the owner');
Paramètres du projet
Comme Protostar ne supporte pas encore le compilateur v2, cet article s'appuiera sur la pré-version Scarb (version 0.5.0-alpha.1) qui le supporte. Pour installer cette version spécifique de Scarb, vous pouvez utiliser la commande suivante.
Une fois la configuration terminée, nous pouvons nous diriger vers src/lib.cairo et commencer à écrire des contrats intelligents.
Stockage et constructeur
Dans la version 2 du compilateur Cairo, les contrats intelligents sont toujours définis par des modules annotés avec un attribut contract, mais cette fois l'attribut est nommé d'après le plugin qui le définit, dans ce cas starknet.
#[starknet::contract]mod Propriétaire {}
Le stockage interne est toujours défini comme une structure qui doit s'appeler Stockage, mais cette fois-ci il doit être annoté avec un attribut de stockage.
Pour définir le constructeur, nous annotons la fonction avec l'attribut constructeur, comme nous l'avons fait dans la v1, l'avantage est que maintenant la fonction peut avoir n'importe quel nom, elle n'a pas besoin d'être appelée "constructeur" comme dans la v1. Bien que ce ne soit pas obligatoire, je ferai toujours référence à la fonction en tant que "constructeur" par convention, mais vous pouvez l'appeler différemment.
Un autre changement important est que maintenant le constructeur passe automatiquement une référence à ContractState, qui agit comme un intermédiaire pour stocker les variables, dans ce cas « propriétaire ».
Notez que la syntaxe pour l'écriture et la lecture du stockage a changé depuis la v1. Avant nous faisions owner::write(), maintenant nous faisons self.owner.write(). Il en va de même pour la lecture à partir du stockage.
Soit dit en passant, le type ContractState n'a pas besoin d'être mis en portée manuellement, il est inclus dans le prélude.
Méthodes publiques
Une différence importante par rapport à la version 1 du compilateur Cairo est que nous devons maintenant définir explicitement l'interface publique d'un contrat intelligent en utilisant des traits annotés avec l'attribut starknet::interface.
Si vous vous souvenez du code original de la v1, notre contrat intelligent a deux méthodes "publiques" (get_owner et transfer_ownership) et une méthode "privée" (uniquement_owner). Cette fonctionnalité ne traite que des méthodes publiques et ne s'appuie pas sur les attributs "external" ou "view" pour indiquer quelles méthodes peuvent modifier l'état du contrat et quelles méthodes ne le peuvent pas. Au lieu de cela, cela est maintenant rendu explicite par le type du paramètre self.
Si une méthode nécessite une référence à ContractStorage (ce qui, une fois implémenté, le T générique le fait), cette méthode est capable de modifier l'état interne du contrat intelligent. C'est ce que nous avions l'habitude d'appeler une méthode "externe". D'autre part, si une méthode nécessite un instantané de ContractStorage, elle ne peut que le lire, pas le modifier. C'est ce que nous avions l'habitude d'appeler la méthode "view".
Nous pouvons maintenant créer une implémentation pour le trait que nous venons de définir en utilisant le mot-clé impl. N'oubliez pas que Cairo diffère de Rust en ce que les implémentations ont des noms.
Nous créons une implémentation pour notre trait à l'intérieur du module définissant le contrat intelligent, en passant le type ContractState en tant que type générique T afin que le stockage soit accessible comme un constructeur.
Notre implémentation est annotée avec l'attribut external(v0). La version 0 dans l'attribut signifie que le sélecteur est uniquement dérivé du nom de la méthode, comme c'était le cas dans le passé. L'inconvénient est que si vous définissez une autre implémentation d'un trait différent pour votre contrat intelligent et que deux traits utilisent le même nom pour l'une de leurs méthodes, le compilateur générera une erreur à cause du sélecteur en double.
Une future version de cette propriété ajoutera peut-être une nouvelle façon d'évaluer les sélecteurs pour éviter les conflits, mais cela ne fonctionne pas encore. Actuellement, nous ne pouvons utiliser que la version 0 des propriétés externes.
Méthodes privées
Nous devons également définir une autre méthode pour le contrat intelligent, only_owner. Cette méthode vérifie si la personne qui l'appelle doit être le propriétaire du contrat intelligent.
Comme il s'agit d'une méthode privée qui ne peut pas être appelée de l'extérieur, elle ne peut pas être définie dans le cadre de OwnableTrait (l'interface publique du contrat intelligent). Au lieu de cela, nous utiliserons l'attribut generate_trait pour créer une nouvelle implémentation du trait généré automatiquement.
...#[starknet::contract]mod Ownable { ... #[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), 'Caller n'est pas le propriétaire'); }
La méthode only_owner peut maintenant être utilisée en appelant self.only_owner() si nécessaire.
Dans Cairo v1, un événement n'était qu'une fonction sans corps et annotée avec l'attribut d'événement, tandis que dans la v2, un événement est une énumération annotée avec le même attribut, mais maintenant implémentée à l'aide de quelques fonctionnalités supplémentaires.
Chaque variante de l'énumération d'événement doit être une structure portant le même nom. Dans cette structure, nous utilisons l'attribut optionnel key pour définir toutes les valeurs que nous voulons émettre, pour informer le système des valeurs que nous voulons que Starknet indexe, afin que l'indexeur puisse rechercher et récupérer plus rapidement. Dans ce cas, nous souhaitons indexer deux valeurs (prev_owner et new_owner).
Le trait ContractState définit une méthode d'émission qui peut être utilisée pour émettre des événements.
Avec cette dernière fonctionnalité, nous avons terminé la migration du contrat intelligent Ownable de la v1 à la v2. Le code complet est présenté ci-dessous.
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), 'Caller n'est pas le propriétaire'); }
Vous pouvez également trouver ce code sur Github.
en conclusion
La version 2 du compilateur Cairo apporte une nouvelle syntaxe à Starknet, rendant le code de contrat intelligent plus cohérent avec Cairo lui-même et, par extension, plus proche de Rust. Même au prix d'un code plus lourd, les avantages en matière de sécurité valent le compromis.
Dans cet article, nous n'avons pas tout abordé sur la nouvelle syntaxe, en particulier son interaction avec d'autres contrats intelligents, mais vous pouvez lire le journal des modifications du compilateur, lire ce message sur le forum ou regarder une vidéo sur la chaîne YouTube de StarkWare pour en savoir plus. à ce sujet pour en savoir plus.
Cette nouvelle version du compilateur sera disponible sur le testnet de Starknet dans quelques semaines et sur le réseau principal dans quelques semaines, alors n'essayez pas de déployer ce code pour l'instant, cela ne fonctionnera pas encore.
Le Caire ne cesse de s'améliorer.
Ressource
Syntaxe du contrat - Guide de migration
Cairo 1 : la syntaxe des contrats évolue
Voir l'original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Interprétation complète de la grammaire améliorée de Starknet
Original : Syntaxe Starknet améliorée
Traduction et relecture : "Communauté chinoise Starknet"
aperçu
La version 2 du compilateur Cairo introduit des changements dans la syntaxe Starknet pour rendre le code plus explicite et sûr. L'interface publique du contrat intelligent est définie à l'aide de traits, et l'accès au stockage se fait via le trait ContractState. Les méthodes privées doivent être définies avec une implémentation différente de l'interface publique. Les événements sont désormais définis comme des énumérations, où chaque variante est une structure du même nom.
Avis de non-responsabilité : les termes utilisés ici font référence à différentes versions du compilateur Cairo, et la syntaxe est provisoire car la communauté Starknet débat toujours du meilleur terme à utiliser. Une fois déterminé, cet article sera mis à jour en conséquence.
Le compilateur v2
La semaine dernière, une nouvelle version majeure 2.0.0-rc0 du compilateur Cairo a été publiée sur Github. Le nouveau compilateur apporte des améliorations significatives au plugin Starknet, rendant notre code plus sûr, plus explicite et plus réutilisable. Notez que cette nouvelle version du compilateur n'est pas encore prise en charge sur Starknet testnet ou mainnet car elle est toujours en cours d'élaboration dans l'environnement intégré.
Le but de cet article est de vous montrer comment réécrire un contrat intelligent Starknet créé pour la version 1.x du compilateur Cairo en un contrat intelligent compatible avec la version 2.x du compilateur. Notre point de départ est le contrat intelligent Ownable créé dans l'article précédent, qui est compatible avec la version 1.x du compilateur Cario.
[contract] mod Ownable {use starknet::ContractAddress;use starknet::get_caller_address;
[event] fn OwnershipTransferred(ancien_propriétaire : ContractAddress, nouveau_owner : ContractAddress) {}
struct Storage {propriétaire : ContractAddress,}
[constructor] fn constructor() {let deployer = get_caller_address();owner::write(deployer);}
[view] fn get_owner() -> ContractAddress {owner::read()}
[external] fn transfer_ownership(new_owner: ContractAddress) {only_owner();let previous_owner = owner::read();owner::write(new_owner);OwnershipTransferred(previous_owner, new_owner) ;}
fn only_owner() {let caller = get_caller_address();assert(caller == owner::read(), 'Caller is not the owner');
Paramètres du projet
Comme Protostar ne supporte pas encore le compilateur v2, cet article s'appuiera sur la pré-version Scarb (version 0.5.0-alpha.1) qui le supporte. Pour installer cette version spécifique de Scarb, vous pouvez utiliser la commande suivante.
$ --proto '=https' --tlsv1.2 -sSf | bash -s -- -v 0.5.0-alpha.1
Une fois l'installation terminée, vérifiez que vous disposez de la bonne version.
$ scarb --version>>>scarb 0.5.0-alpha.1 (546dad33d 2023-06-19)cairo:2.0.0-rc3()
Un projet Scarb peut maintenant être créé.
$ scarb nouveau cairo1_v2$cdcairo1_v2
Vous devriez obtenir une structure de dossiers comme ci-dessous.
$ arbre .>>>.├── Scarb.toml└── src└──lib.cairo
Pour que Scarb compile les contrats intelligents Starknet, le plugin Starknet doit être activé en tant que dépendance.
// Scarb.toml... [dependencies] starknet="2.0.0-rc3"
Une fois la configuration terminée, nous pouvons nous diriger vers src/lib.cairo et commencer à écrire des contrats intelligents.
Stockage et constructeur
Dans la version 2 du compilateur Cairo, les contrats intelligents sont toujours définis par des modules annotés avec un attribut contract, mais cette fois l'attribut est nommé d'après le plugin qui le définit, dans ce cas starknet.
#[starknet::contract]mod Propriétaire {}
Le stockage interne est toujours défini comme une structure qui doit s'appeler Stockage, mais cette fois-ci il doit être annoté avec un attribut de stockage.
#[starknet::contract]mod Ownable {use super::ContractAddress; # [storage] struct Storage {propriétaire : ContractAddress,
Pour définir le constructeur, nous annotons la fonction avec l'attribut constructeur, comme nous l'avons fait dans la v1, l'avantage est que maintenant la fonction peut avoir n'importe quel nom, elle n'a pas besoin d'être appelée "constructeur" comme dans la v1. Bien que ce ne soit pas obligatoire, je ferai toujours référence à la fonction en tant que "constructeur" par convention, mais vous pouvez l'appeler différemment.
Un autre changement important est que maintenant le constructeur passe automatiquement une référence à ContractState, qui agit comme un intermédiaire pour stocker les variables, dans ce cas « propriétaire ».
#[starknet::contract]mod Ownable {use super::ContractAddress; # [storage] struct Storage {propriétaire : ContractAddress,} # [constructor] constructeur fn (ref self : ContractState) {let deployer = get_caller_address();self.owner.write(deployer);
Notez que la syntaxe pour l'écriture et la lecture du stockage a changé depuis la v1. Avant nous faisions owner::write(), maintenant nous faisons self.owner.write(). Il en va de même pour la lecture à partir du stockage.
Soit dit en passant, le type ContractState n'a pas besoin d'être mis en portée manuellement, il est inclus dans le prélude.
Méthodes publiques
Une différence importante par rapport à la version 1 du compilateur Cairo est que nous devons maintenant définir explicitement l'interface publique d'un contrat intelligent en utilisant des traits annotés avec l'attribut starknet::interface.
utilisez starknet::ContractAddress ;
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self : T, new_owner : ContractAddress); fn get_owner(self : @T) -> ContractAddress ;}
#[starknet::contract]mod Propriétaire { ...}
Si vous vous souvenez du code original de la v1, notre contrat intelligent a deux méthodes "publiques" (get_owner et transfer_ownership) et une méthode "privée" (uniquement_owner). Cette fonctionnalité ne traite que des méthodes publiques et ne s'appuie pas sur les attributs "external" ou "view" pour indiquer quelles méthodes peuvent modifier l'état du contrat et quelles méthodes ne le peuvent pas. Au lieu de cela, cela est maintenant rendu explicite par le type du paramètre self.
Si une méthode nécessite une référence à ContractStorage (ce qui, une fois implémenté, le T générique le fait), cette méthode est capable de modifier l'état interne du contrat intelligent. C'est ce que nous avions l'habitude d'appeler une méthode "externe". D'autre part, si une méthode nécessite un instantané de ContractStorage, elle ne peut que le lire, pas le modifier. C'est ce que nous avions l'habitude d'appeler la méthode "view".
Nous pouvons maintenant créer une implémentation pour le trait que nous venons de définir en utilisant le mot-clé impl. N'oubliez pas que Cairo diffère de Rust en ce que les implémentations ont des noms.
utilisez starknet::ContractAddress ;
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self : T, new_owner : ContractAddress); fn get_owner(self : @T) -> ContractAddress ;}
#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self : ContractState, new_owner : ContractAddress) { let prev_owner = self.owner.read(); self.owner.write(nouveau_propriétaire); }
fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() }
Nous créons une implémentation pour notre trait à l'intérieur du module définissant le contrat intelligent, en passant le type ContractState en tant que type générique T afin que le stockage soit accessible comme un constructeur.
Notre implémentation est annotée avec l'attribut external(v0). La version 0 dans l'attribut signifie que le sélecteur est uniquement dérivé du nom de la méthode, comme c'était le cas dans le passé. L'inconvénient est que si vous définissez une autre implémentation d'un trait différent pour votre contrat intelligent et que deux traits utilisent le même nom pour l'une de leurs méthodes, le compilateur générera une erreur à cause du sélecteur en double.
Une future version de cette propriété ajoutera peut-être une nouvelle façon d'évaluer les sélecteurs pour éviter les conflits, mais cela ne fonctionne pas encore. Actuellement, nous ne pouvons utiliser que la version 0 des propriétés externes.
Méthodes privées
Nous devons également définir une autre méthode pour le contrat intelligent, only_owner. Cette méthode vérifie si la personne qui l'appelle doit être le propriétaire du contrat intelligent.
Comme il s'agit d'une méthode privée qui ne peut pas être appelée de l'extérieur, elle ne peut pas être définie dans le cadre de OwnableTrait (l'interface publique du contrat intelligent). Au lieu de cela, nous utiliserons l'attribut generate_trait pour créer une nouvelle implémentation du trait généré automatiquement.
...#[starknet::contract]mod Ownable { ... #[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), 'Caller n'est pas le propriétaire'); }
La méthode only_owner peut maintenant être utilisée en appelant self.only_owner() si nécessaire.
#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self : ContractState, new_owner : ContractAddress) { self.only_owner (); ... } ... }
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { ... }
événement
Dans Cairo v1, un événement n'était qu'une fonction sans corps et annotée avec l'attribut d'événement, tandis que dans la v2, un événement est une énumération annotée avec le même attribut, mais maintenant implémentée à l'aide de quelques fonctionnalités supplémentaires.
...#[starknet::contract]mod Propriétaire { ... # [event] #[derive(Drop, starknet::Event)] enum Event { OwnershipTransferred : OwnershipTransferred, }
#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: ContractAddress, # [key] nouveau_propriétaire : ContractAddress,
Chaque variante de l'énumération d'événement doit être une structure portant le même nom. Dans cette structure, nous utilisons l'attribut optionnel key pour définir toutes les valeurs que nous voulons émettre, pour informer le système des valeurs que nous voulons que Starknet indexe, afin que l'indexeur puisse rechercher et récupérer plus rapidement. Dans ce cas, nous souhaitons indexer deux valeurs (prev_owner et new_owner).
Le trait ContractState définit une méthode d'émission qui peut être utilisée pour émettre des événements.
...#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { .. .self.emit(Event::OwnershipTransferred(OwnershipTransferred { prev_owner: prev_owner, new_owner: new_owner, })); } ... } ...}
Avec cette dernière fonctionnalité, nous avons terminé la migration du contrat intelligent Ownable de la v1 à la v2. Le code complet est présenté ci-dessous.
utilisez starknet::ContractAddress ;
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self : T, new_owner : ContractAddress); fn get_owner(self : @T) -> ContractAddress ;}
#[starknet::contract]mod Ownable { use super::ContractAddress; utilisez starknet::get_caller_address ;
[event] #[derive(Drop, starknet::Event)] enum Event { OwnershipTransferred : OwnershipTransferred, }
#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: ContractAddress, # [key] nouveau_propriétaire : ContractAddress, }
[storage] struct Storage { propriétaire : ContractAddress, }
[constructor] constructeur fn (ref self : ContractState) { let deployer = get_caller_address(); self.owner.write(déployeur); }
#[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self : ContractState, new_owner : ContractAddress) { self.only_owner(); let prev_owner = self.owner.read(); self.owner.write(nouveau_propriétaire); self.emit(Event::OwnershipTransferred(OwnershipTransferred { prev_owner: prev_owner, new_owner: new_owner, })); }
fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() } }
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), 'Caller n'est pas le propriétaire'); }
Vous pouvez également trouver ce code sur Github.
en conclusion
La version 2 du compilateur Cairo apporte une nouvelle syntaxe à Starknet, rendant le code de contrat intelligent plus cohérent avec Cairo lui-même et, par extension, plus proche de Rust. Même au prix d'un code plus lourd, les avantages en matière de sécurité valent le compromis.
Dans cet article, nous n'avons pas tout abordé sur la nouvelle syntaxe, en particulier son interaction avec d'autres contrats intelligents, mais vous pouvez lire le journal des modifications du compilateur, lire ce message sur le forum ou regarder une vidéo sur la chaîne YouTube de StarkWare pour en savoir plus. à ce sujet pour en savoir plus.
Cette nouvelle version du compilateur sera disponible sur le testnet de Starknet dans quelques semaines et sur le réseau principal dans quelques semaines, alors n'essayez pas de déployer ce code pour l'instant, cela ne fonctionnera pas encore.
Le Caire ne cesse de s'améliorer.
Ressource