Un aperçu des meilleures pratiques, modèles et anti-modèles
Tout au long de cette série d’articles, nous avons vous a présenté la blockchain Flowson langage de contrat intelligent Cadenceet certains des plus outils essentiels que les développeurs doivent connaîtretout en comparant et en contrastant avec Ethereum.
Dans cet article, nous parlerons des meilleures pratiques et des modèles à suivre lors de l’utilisation du langage Cadence et du développement de solutions sur le réseau Flow, ainsi que des modèles à éviter.
Utiliser le réseau de test Flow et l’émulateur Flow
L’une des étapes les plus importantes du développement d’une blockchain consiste à tester vos projets dans un environnement simulé. La blockchain de flux n’est pas différente. A cet effet, le Réseau de test de flux et Émulateur de flux sont des éléments cruciaux à inclure dans votre processus de développement. Non seulement ils vous permettent de comprendre comment votre dapp fonctionnera, mais ils vous aideront également à détecter les bogues critiques avant le déploiement sur le réseau principal.
Utiliser des contrats standards
Deux des cas d’utilisation les plus courants de toute blockchain sont les monnaies numériques et la fourniture d’une preuve de propriété numérique via les NFT. Au lieu d’essayer de réécrire les contrats nécessaires à partir de zéro, les développeurs de Flow doivent toujours importer les contrats de base officiels pour assurer la sécurité des utilisateurs et la cohérence avec le reste du réseau.
Il existe actuellement trois contrats standard que vous devez utiliser :
Ces contrats sont essentiellement des interfaces qui obligent quiconque les implémente à ajouter leurs déclarations de variables et de méthodes, ce qui leur permet d’être interopérables avec d’autres contrats intelligents de l’écosystème Flow. Ils existent déjà sur les réseaux testnet et mainnet, alors assurez-vous d’importer à partir des adresses officielles liées ci-dessus lors de leur mise en œuvre dans votre contrat.
Vous pouvez trouver d’autres contrats de base dans la documentation du flux.
Créez des tests pour vos contrats, transactions et scripts intelligents
La Cadre de test JavaScript de flux est crucial pour tester les scénarios de déploiement de vos contrats. Cependant, il est important de noter que vous aurez besoin du CLI de flux fonctionnant en arrière-plan pour une fonctionnalité complète. Avec ceux-ci, vous pouvez tester la création de nouveaux comptes, l’envoi de transactions, l’exécution de requêtes, l’exécution de scripts, etc. De plus, il s’intègre à Flow Emulator pour créer, exécuter et arrêter des instances d’émulateur.
Ajoutez votre contrat au catalogue Flow NFT
La Catalogue Flow NFT existe en tant que base de données pour stocker les contrats et les métadonnées NFT. En téléchargeant votre contrat, vos NFT deviennent interopérables avec le reste de l’écosystème Flow, et d’autres développeurs peuvent facilement prendre en charge votre collection sur leurs places de marché ou d’autres dapps.
Tu peux ajouter votre contrat au catalogue en suivant un processus simple en quatre étapes qui nécessite de sélectionner un contrat, d’obtenir des informations supplémentaires, d’examiner les métadonnées, puis d’ajouter le Flow NFT au catalogue.
Soyez précis avec les types
La cadence est un fort et statiquement langage typé qui permet au développeur de spécifier quels types contiennent ou renvoient des variables, des interfaces et des fonctions. Par conséquent, vous devez être aussi précis que possible lorsque vous faites des déclarations ; n’utilisez les types génériques que dans les situations nécessaires. Ne pas le faire peut entraîner des erreurs maladroites.
Contrôle d’accès
Dans la mesure du possible, vous devez être délibéré avec le contrôle d’accès. Heureusement, sur la blockchain Flow, un appelant ne peut pas accéder aux objets dans le stockage du compte d’un autre utilisateur sans y faire référence. C’est ce qu’on appelle la sécurité basée sur les références, et cela signifie que rien n’est vraiment public par défaut.
Cependant, lors de l’écriture du code Cadence, des précautions doivent être prises lors de la déclaration des variables, des structures, des fonctions, des ressources, etc. Il existe quatre niveaux de contrôle d’accès qu’un développeur peut inclure dans ses déclarations :
pub/access(all)
— accessible/visible dans tous les domainesaccess(account)
— accessible uniquement dans l’ensemble du compte où il est défini (les autres contrats du même compte peuvent y accéder)access(contract)
— accessible uniquement dans le cadre du contrat dans lequel il est défini (non accessible en dehors du contrat)priv/access(self)
— accessible uniquement dans les portées actuelles et intérieures
Évitez d’utiliser pub/access(all) dans la mesure du possible. Consultez la documentation Flow pour plus d’informations sur le contrôle d’accès.
Créez des constantes StoragePath et PublicPath dans votre contrat
Les chemins d’accès aux ressources de votre contrat sont extrêmement importants et doivent être cohérents dans toutes les transactions et tous les scripts. Pour assurer l’uniformité, vous devez créer des constantes pour les deux PublicPath
et StoragePath
.
pub contract BestPractices {
pub let CollectionStoragePath: StoragePath
pub let CollectionPublicPath: PublicPath
init(){
self.CollectionStoragePath = /storage/bestPracticesCollection
self.CollectionPublicPath = /public/bestPracticesCollection
}
}
Lorsque vous créez une transaction qui utilise l’un de ces chemins, il vous suffit d’appeler sa variable respective avec le contrat importé pour référencer le chemin requis.
//This script checks if a user has the public Collection from BestPractices contract
import BestPractices from 0x...
pub fun main(addr: Address): Bool {
return getAccount(addr).getLinkTarget(BestPractices.CollectionPublicPath) != nil
}
Ressource d’administration
Il est recommandé de créer une ressource administrative qui contient des fonctions spécifiques pour effectuer des actions dans le cadre du contrat, comme une fonction de menthe. Cette convention garantit que seuls les comptes disposant de cette ressource ou capacité peuvent exécuter des fonctions administratives.
pub contract BestPractices {
pub let CollectionStoragePath: StoragePath
pub let CollectionPublicPath: PublicPath
pub let AdminStoragePath: StoragePath
pub resource AdminResource {
pub fun mintNFT(){
//your mint NFT code here!
}
} init(){
self.CollectionStoragePath = /storage/bestPracticesCollection
self.CollectionPublicPath = /public/bestPracticesCollection
self.AdminStoragePath = /storage/bestPracticesAdmin//Create the adminResource and store it inside Contract Deployer account
let adminResource <- create AdminResource()
self.account.save(<- adminResource, to: self.AdminStoragePath)
}
}
Créer des événements personnalisés
Les événements sont des valeurs qui peuvent être émises lors de l’exécution de votre code Cadence. Par exemple, lors de la définition d’actions importantes dans vos contrats, vous pouvez émettre des événements pour signaler leur réalisation ou délivrer une valeur spécifique. Par conséquent, les transactions qui interagissent avec vos contrats intelligents peuvent recevoir des informations supplémentaires via ces événements.
pub contract BestPractices {
pub let CollectionStoragePath: StoragePath
pub let CollectionPublicPath: PublicPath
pub let AdminStoragePath: StoragePath
//Create your own events
pub event AdminCreated() pub resource AdminResource {
pub fun mintNFT(){
//your mint NFT code here!
}
} init(){
self.CollectionStoragePath = /storage/bestPracticesCollection
self.CollectionPublicPath = /public/bestPracticesCollection
self.AdminStoragePath = /storage/bestPracticesAdmin//Create the adminResource and store it inside Contract Deployer account
let adminResource <- create AdminResource()
self.account.save(<- adminResource, to: self.AdminStoragePath)
}
}
Soyez précis avec les types dans les contraintes de type
L’une des fonctionnalités les plus puissantes du langage Cadence est sans aucun doute capacités. Grâce aux capacités, la portée de l’accès aux ressources s’élargit.
Un point important lors de la création d’une fonctionnalité consiste à spécifier les fonctionnalités de votre ressource qui doivent être disponibles pour les autres. Cela peut être fait au moment de la création du lien en utilisant des contraintes de type.
Dans cet exemple, nous utilisons le Exemple de contrat NFT pour créer une fonctionnalité de base où tout compte qui souhaite recevoir un ExampleNFT doit avoir une collection.
import NonFungibleToken from 0x...
import ExampleNFT from 0x...
transaction{
prepare(acct: AuthAccount){
let collection <- ExampleNFT.createEmptyCollection()
// Put the new collection in storage
acct.save(<-collection, to: ExampleNFT.CollectionStoragePath)
// Create a public Capability for the collection
acct.link<&ExampleNFT.Collection{ExampleNFT.CollectionPublic}>(ExampleNFT.CollectionPublicPath, target: ExampleNFT.CollectionStoragePath)
}
}
La &
symbole dans &ExampleNFT
précise que nous utilisons un référence. Après le symbole de référence, nous ajoutons le type auquel la capacité que nous créons aura accès. À ce stade, nous devons être aussi précis que possible.
Ce modèle renforce la sécurité et limite la fonctionnalité que l’utilisateur appelant borrow
fonction de cette capacité peut utiliser.
Omettre le {ExampleNFT.CollectionPublic}
type vous donnera accès à toutes les fonctions du ExampleNFT.Collection
référence, y compris la fonction de retrait, afin que n’importe qui puisse accéder à la collection de l’utilisateur et voler ses NFT.
Utiliser la fonction d’emprunt
Pour utiliser les fonctionnalités de la ressource, vous pouvez appeler le Load
pour supprimer la ressource du compte, utiliser ses fonctionnalités et appeler la Save
fonction pour le sauvegarder à nouveau. Cependant, cette approche est coûteuse et inefficace.
Pour éviter cela, utilisez le borrow
fonction à la place. Il vous permet d’utiliser une référence à la ressource que vous appelez. Cette méthode rend votre transaction beaucoup plus efficace et rentable.
Utiliser les fonctions check et getLinkTarget
Lors de la création d’applications sur la blockchain Flow, vous découvrirez que le compte de l’utilisateur joue un rôle essentiel. Contrairement à d’autres blockchains, telles qu’Ethereum, Flow stocke les ressources, les actifs et plus directement dans le compte de l’utilisateur plutôt que comme référence à une adresse sur un grand livre numérique public.
Cette approche nécessite que le compte dispose d’un emplacement de stockage spécifique, tel qu’une collection ou un coffre-fort pour les jetons fongibles. Cependant, cela ajoute également de la complexité. Il faut être sûr que l’utilisateur a ou n’a pas la collection dans son compte.
Les deux check()
fonction (qui vérifie si une capacité existe dans un chemin donné) et la fonction getLinkTarget()
La fonction (qui renvoie le chemin d’une fonctionnalité donnée) doit être utilisée lors de l’ajout de collections et de fonctionnalités au compte d’utilisateur. Ces fonctions garantissent que la transaction s’exécute sans problème.
Utilisez la panique
La panique est une fonction intégrée à Cadence qui vous permet de mettre fin à un programme sans condition. Cela peut se produire lors de l’exécution de votre code de contrat intelligent et renvoie un message d’erreur, ce qui permet de comprendre plus facilement quand quelque chose ne se passe pas comme prévu.
Lors de la déclaration de variables, il est possible de les définir comme facultatives, c’est-à-dire que si elles ne sont pas du type spécifié, elles ont la valeur nil.
Ainsi, dans Cadence, il est possible d’utiliser deux points d’interrogation suivis du panic("treatment message")
fonction lors de l’interrogation de la valeur d’une variable ou d’une fonction particulière qui renvoie une option.
let optionalAccount: AuthAccount? = //...
let account = optionalAccount ?? panic("missing account")
Cette commande ??panic("treatment message")
tente de renvoyer la valeur avec le type spécifié. Si la valeur renvoyée est du mauvais type, ou nulle, l’exécution est abandonnée et le message de traitement sélectionné s’affiche sur la console.
Bien que Cadence soit conçu pour éviter de nombreux bugs et exploits potentiels trouvés dans d’autres écosystèmes de blockchain, il existe des développeurs anti-modèles dont ils doivent être conscients lors de la construction. Vous trouverez ci-dessous quelques éléments importants à prendre en compte. Pour une liste complète des anti-modèles, consultez la documentation de flux.
Ne pas spécifier le type lors de l’utilisation de la fonction d’emprunt
Les développeurs doivent utiliser la fonction d’emprunt mentionnée ci-dessus pour tirer parti des fonctionnalités disponibles dans une capacité. Cependant, il devrait être clair que les utilisateurs peuvent stocker n’importe quoi dans leur mémoire. Par conséquent, il est essentiel de s’assurer que ce qui est emprunté est du bon type.
Ne pas spécifier le type est un anti-modèle qui peut finir par provoquer des erreurs ou même des ruptures dans votre transaction et votre application.
//Bad Practice. Should be avoided
let collection = getAccount(address).getCapability(ExampleNFT.CollectionPublicPath)
.borrow<&ExampleNFT.Collection>()?? panic("Could not borrow a reference to the nft collection")
//Correct!
let collection = getAccount(address).getCapability(ExampleNFT.CollectionPublicPath)
.borrow<&ExampleNFT.Collection{NonFungibleToken.CollectionPublic}>()
?? panic("Could not borrow a reference to the nft collection")
Utiliser AuthAccount comme paramètre de fonction
Pour une transaction en phase de préparation, il est possible d’accéder au AuthAccount
domaine de l’utilisateur. Cet objet permet l’accès à la mémoire de stockage et à tous les autres espaces privés du compte pour l’utilisateur qui le fournit.
Passer ce champ comme argument n’est pas nécessaire et doit être évité, car les cas où cette méthode est nécessaire sont extrêmement rares.
//Bad Practice. Should be avoided
transaction() {
prepare(acct: AuthAccount){
//You take sensitive and important user data out of the scope of the transaction prepare phase
ExampleNFT.function(acct: acct)
}
}
Utilisation du contrôle d’accès public sur les dictionnaires et les tableaux
En stockant des dictionnaires et des variables de tableau dans votre contrat avec pub/access(all)
portée, votre contrat intelligent devient vulnérable, car n’importe qui peut manipuler et modifier ces valeurs.
Création de capacités et de références à l’aide du mot-clé auth
Créer des capacités et des références avec le auth
Le mot-clé expose la valeur à la conversion descendante, ce qui pourrait donner accès à des fonctionnalités qui n’étaient pas prévues à l’origine.
//AVOID THIS!
signer.link<auth &ExampleNFT.CollectionPublic{NonFungibleToken.Receiver}>(
/public/exampleNFT,
target: /storage/exampleNFT
)
Lors du développement sur la blockchain Flow à l’aide de Cadence, il est utile de connaître les modèles de conception, les anti-modèles et les meilleures pratiques.
En suivant ces bonnes pratiques, nous pouvons garantir un niveau de sécurité constant dans l’ensemble de l’écosystème Flow, offrant ainsi la meilleure expérience aux utilisateurs. Pour une compréhension plus approfondie, lire la documentation de Flow.
Passez une très bonne journée !