diff --git a/public/content/translations/fr/developers/tutorials/how-to-implement-an-erc721-market/index.md b/public/content/translations/fr/developers/tutorials/how-to-implement-an-erc721-market/index.md index 4ae29d0236c..1e366b8d5b0 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-implement-an-erc721-market/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-implement-an-erc721-market/index.md @@ -1,12 +1,14 @@ --- -title: Comment mettre en œuvre un marché ERC-721 -description: Comment mettre en vente des objets tokénisés dans un forum de petites annonces décentralisé +title: "Comment mettre en œuvre un marché ERC-721" +description: "Comment mettre en vente des objets tokénisés dans un forum de petites annonces décentralisé" author: "Alberto Cuesta Cañada" tags: - - "contrats intelligents" - - "erc-721" - - "solidity" - - "jetons" + [ + "contrats intelligents", + "erc-721", + "solidité", + "jetons" + ] skill: intermediate lang: fr published: 2020-03-19 @@ -26,13 +28,13 @@ Avec la blockchain, ces marchés sont appelés à changer une fois de plus. Lais Le business model d'un forum public de petites annonces sur la blockchain devra être différent de celui d'Ebay et d'une entreprise. -Tout d'abord, il y a [la qestion de la décentralisation](/developers/docs/web2-vs-web3/). Les plateformes existantes doivent assurer le bon fonctionnement de leurs propres serveurs. Le bon fonctionnement d'une plateforme décentralisée est assuré par ses utilisateurs, de sorte que le coût de fonctionnement de la plateforme centrale tombe à zéro pour le propriétaire de la plateforme. +Tout d'abord, il y a [l'angle de la décentralisation](/developers/docs/web2-vs-web3/). Les plateformes existantes doivent assurer le bon fonctionnement de leurs propres serveurs. Le bon fonctionnement d'une plateforme décentralisée est assuré par ses utilisateurs, de sorte que le coût de fonctionnement de la plateforme centrale tombe à zéro pour le propriétaire de la plateforme. -Il y a ensuite le front-end, le site Web ou l'interface qui donne accès à la plateforme. Les options sont nombreuses. Les propriétaires d'une plateforme peuvent en restreindre l'accès et obliger tout le monde à utiliser leur interface, en facturant des frais. Ils peuvent également décider d'en ouvrir l'accès (Power to the People !) et laisser n'importe qui créer des interfaces avec la plateforme. Ou encore décider d'une approche située entre ces deux extrêmes. +Il y a ensuite le front-end, le site Web ou l'interface qui donne accès à la plateforme. Les options sont nombreuses. Les propriétaires d'une plateforme peuvent en restreindre l'accès et obliger tout le monde à utiliser leur interface, en facturant des frais. Les propriétaires de la plateforme peuvent également décider d'ouvrir l'accès (Le pouvoir au peuple !) et laisser n'importe qui construire des interfaces pour la plateforme. Ou encore décider d'une approche située entre ces deux extrêmes. _Les chefs d'entreprise qui ont plus de vision que moi sauront comment monétiser cela. Tout ce que je vois, c'est que c'est différent du statu quo et probablement rentable._ -Il y a aussi la question de l'automatisation et des paiements. Certaines choses peuvent être [tokénisées très efficacement](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) et échangées dans un forumde petites annonces. Les actifs tokénisés sont faciles à céder dans une blockchain. Des méthodes de paiement très complexes peuvent être facilement mises en œuvre dans une blockchain. +Il y a aussi la question de l'automatisation et des paiements. Certaines choses peuvent être très efficacement [tokenisées](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) et échangées sur un tableau de petites annonces. Les actifs tokénisés sont faciles à céder dans une blockchain. Des méthodes de paiement très complexes peuvent être facilement mises en œuvre dans une blockchain. Je sens juste ici une occasion de faire des affaires. Il est facile de mettre en place un tableau de petites annonces sans frais de fonctionnement, avec des modalités de paiement complexes incluses pour chaque transaction. Je suis sûr que quelqu'un trouvera une idée pour concrétiser le tout. @@ -40,9 +42,9 @@ Je suis ravi de m'atteler à la tâche. Jetons un coup d'oeil au code. ## Implémentation {#implementation} -Il y a quelque temps, nous avons lancé un [référentiel de sources ouvertes](https://github.com/HQ20/contracts?ref=hackernoon.com) avec des exemples de concrétiser d'opportunités commerciales et d'autres goodies, veuillez y jeter un coup d'œil. +Il y a quelque temps, nous avons lancé un [dépôt open source](https://github.com/HQ20/contracts?ref=hackernoon.com) contenant des exemples d'implémentation de cas d'entreprise et d'autres surprises, n'hésitez pas à le consulter. -Le code pour ce [forum de petites annonces Ethereum](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) est là, n'hésitez pas à l'utiliser et à en abuser. Sachez simplement que le code n'a pas été vérifié et que vous devez faire votre propre recherche avant de laisser de l'argent y entrer. +Le code pour ce [tableau de petites annonces Ethereum](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) s'y trouve, veuillez l'utiliser et en abuser. Sachez simplement que le code n'a pas été vérifié et que vous devez faire votre propre recherche avant de laisser de l'argent y entrer. Les principes de base du conseil ne sont pas complexes. Toutes les annonces dans le forum se résumeront juste à une structure avec quelques champs : @@ -67,9 +69,9 @@ Utiliser un mapping implique simplement de trouver un identifiant pour chaque an Vient ensuite la question de savoir quels sont ces articles que nous traitons, et quelle est cette monnaie qui est utilisée pour payer la transaction. -Pour les articles, nous allons simplement demander la mise en œuvre de l'interface [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com), qui n'est en fait qu'un moyen de représenter des articles du monde réel dans une blockchain, bien qu'elle [fonctionne mieux avec les actifs numériques](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com). Nous allons spécifier notre propre contrat ERC721 dans le constructeur, ce qui signifie que tous les actifs publiées dans notre forum de petites annonces doivent avoir été tokénisés au préalable. +Pour les articles, nous allons simplement demander qu'ils implémentent l'interface [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com), qui est en fait simplement une manière de représenter des objets du monde réel sur une blockchain, bien que cela [fonctionne mieux avec des actifs numériques](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com). Nous allons spécifier notre propre contrat ERC721 dans le constructeur, ce qui signifie que tous les actifs publiées dans notre forum de petites annonces doivent avoir été tokénisés au préalable. -Pour les paiements, nous allons faire quelque chose de similaire. La plupart des projets de blockchain définissent leur propre cryptomonnaie [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com). D'autres préfèrent utiliser un produit grand public comme DAI. Dans ce forum de petites annonces, il vous suffit de décider, au moment de la construction, quelle sera votre monnaie. Facile. +Pour les paiements, nous allons faire quelque chose de similaire. La plupart des projets blockchain définissent leur propre cryptomonnaie [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com). D'autres préfèrent utiliser un produit grand public comme DAI. Dans ce forum de petites annonces, il vous suffit de décider, au moment de la construction, quelle sera votre monnaie. Facile. ```solidity constructor ( @@ -127,16 +129,16 @@ function cancelTrade(uint256 _trade) Trade memory trade = trades[_trade]; require( msg.sender == trade.poster, - "Trade can be cancelled only by poster." + "L'échange ne peut être annulé que par son auteur." ); - require(trade.status == "Open", "Trade is not Open."); + require(trade.status == "Open", "L'échange n'est pas ouvert."); itemToken.transferFrom(address(this), trade.poster, trade.item); trades[_trade].status = "Cancelled"; emit TradeStatusChange(_trade, "Cancelled"); } ``` -C’est tout. Vous êtes arrivé à la fin de l'implémentation. Il est assez surprenant de constater à quel point certains concepts commerciaux sont compacts lorsqu'ils sont exprimés en code, et c'est l'un de ces cas. Consultez le contrat complet [dans notre référentiel](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol). +C’est tout. Vous êtes arrivé à la fin de l'implémentation. Il est assez surprenant de constater à quel point certains concepts commerciaux sont compacts lorsqu'ils sont exprimés en code, et c'est l'un de ces cas. Consultez le contrat complet [dans notre dépôt](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol). ## Conclusion {#conclusion} @@ -146,4 +148,4 @@ Les forums de petites annonces se révèlent également être un outil facile à Dans cet article, j'ai tenté de faire le lien entre la réalité commerciale d'une entreprise de publication de petites annonces et son implémentation technologique. Ces connaissances devraient vous aider à créer une vision et une feuille de route pour l'implémentation si vous avez les bonnes compétences. -Comme toujours, si vous êtes en train de construire quelque chose d'amusant et que vous souhaitez recevoir des conseils, n'hésitez pas à [m'envoyer un mot](https://albertocuesta.es/) ! Je suis toujours ravi d'aider. +Comme toujours, si vous êtes en train de construire quelque chose d'amusant et que vous souhaitez recevoir des conseils, n'hésitez pas à m'envoyer un mot ! Je suis toujours ravi d'aider. diff --git a/public/content/translations/fr/developers/tutorials/how-to-mint-an-nft/index.md b/public/content/translations/fr/developers/tutorials/how-to-mint-an-nft/index.md index f51ede85368..f1632902a0a 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-mint-an-nft/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-mint-an-nft/index.md @@ -1,38 +1,42 @@ --- -title: Comment frapper un NFT (Partie 2/3 du tutoriel NFT) -description: Ce tutoriel explique comment frapper un NFT sur la blockchain Ethereum grâce à notre contrat intelligent et à Web3. +title: "Comment frapper un NFT (partie 2/3 de la série de tutoriels sur les NFT)" +description: "Ce tutoriel explique comment frapper un NFT sur la blockchain Ethereum grâce à notre contrat intelligent et à Web3." author: "Sumi Mudgil" tags: - - "ERC-721" - - "alchemy" - - "solidity" - - "contrats intelligents" + [ + "ERC-721", + "alchemy", + "solidité", + "contrats intelligents" + ] skill: beginner lang: fr published: 2021-04-22 --- -[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html) : 69 millions de dollars [3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b) : 11 millions de dollars [Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens) : 6 millions de dollars +[Beeple](https://www.nytimes.com/2021/03/11/arts/design/nft-auction-christies-beeple.html) : 69 millions de dollars +[3LAU](https://www.forbes.com/sites/abrambrown/2021/03/03/3lau-nft-nonfungible-tokens-justin-blau/?sh=5f72ef64643b) : 11 millions de dollars +[Grimes](https://www.theguardian.com/music/2021/mar/02/grimes-sells-digital-art-collection-non-fungible-tokens) : 6 millions de dollars -Tous ont frappé leur NFT en utilisant la puissante API d'Alchemy. Dans ce tutoriel, nous vous apprendrons comment faire la même chose en < 10 minutes. +Tous ont frappé leurs NFT en utilisant la puissante API d'Alchemy. Dans ce tutoriel, nous vous apprendrons comment faire la même chose en \<10 minutes. -« Frapper un NFT » (Minting an NFT) est l'acte de publier une instance unique de votre jeton ERC-721 sur la blockchain. En utilisant notre contrat intelligent de la [Partie 1 de cette série de tutoriels sur les NFT](/developers/tutorials/how-to-write-and-deploy-an-nft/), nous allons développer nos compétences en Web3 et frapper un NFT. À la fin de ce tutoriel, vous serez en mesure de frapper autant de NFT que vous, (ou votre portefeuille) le désirez ! +« Frapper un NFT » est l'acte de publier une instance unique de votre jeton ERC-721 sur la blockchain. En utilisant notre contrat intelligent de la [partie 1 de cette série de tutoriels sur les NFT](/developers/tutorials/how-to-write-and-deploy-an-nft/), mettons en pratique nos compétences Web3 et frappons un NFT. À la fin de ce tutoriel, vous serez en mesure de frapper autant de NFT que votre cœur (et votre portefeuille) le désire ! Commençons ! -## Étape 1 : Installer Web3 {#install-web3} +## Étape 1 : Installer Web3 {#install-web3} -Si vous avez suivi le premier tutoriel sur la création de votre contrat intelligent NFT, vous avez déjà expérimenté Ethers.js. Web3 est similaire à Ethers, étant une bibliothèque utilisée pour faciliter la création de requêtes vers la blockchain Ethereum. Dans ce tutoriel, nous utiliserons [Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3) qui est une bibliothèque Web3 améliorée proposant des essais automatiques et une prise en charge solide de WebSocket. +Si vous avez suivi le premier tutoriel sur la création de votre contrat intelligent de NFT, vous avez déjà de l'expérience avec Ethers.js. Web3 est similaire à Ethers, car c'est une bibliothèque utilisée pour faciliter la création de requêtes sur la blockchain Ethereum. Dans ce tutoriel, nous utiliserons [Alchemy Web3](https://docs.alchemyapi.io/alchemy/documentation/alchemy-web3), qui est une bibliothèque Web3 améliorée offrant des relances automatiques et une prise en charge robuste de WebSocket. -Dans le répertoire d'accueil de votre projet, exécutez : +Dans le répertoire principal de votre projet, exécutez : ``` npm install @alch/alchemy-web3 ``` -## Étape 2 : Créer un fichier `mint-nft.js` {#create-mintnftjs} +## Étape 2 : Créer un fichier `mint-nft.js` {#create-mintnftjs} -À l'intérieur de votre répertoire de scripts, créez un fichier `mint-nft.js` et ajoutez les lignes de code suivantes : +Dans votre répertoire `scripts`, créez un fichier `mint-nft.js` et ajoutez les lignes de code suivantes : ```js require("dotenv").config() @@ -41,83 +45,83 @@ const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3(API_URL) ``` -## Étape 3 : Récupérer l'ABI de votre contrat {#contract-abi} +## Étape 3 : Récupérer l'ABI de votre contrat {#contract-abi} -L'ABI (Application Binary Interface) de notre contrat est l’interface permettant d'interagir avec notre contrat intelligent. Vous en apprendrez plus sur les ABI de contrats [ici](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is). Hardhat génère automatiquement pour nous une ABI et l'enregistre dans le fichier `MyNFT.json`. Pour l'utiliser, nous devrons analyser les contenus en ajoutant les lignes de code suivantes à notre fichier `mint-nft.js` : +L'ABI (Application Binary Interface) de notre contrat est l’interface qui permet d'interagir avec notre contrat intelligent. Vous pouvez en apprendre davantage sur les ABI de contrat [ici](https://docs.alchemyapi.io/alchemy/guides/eth_getlogs#what-are-ab-is). Hardhat génère automatiquement un ABI pour nous et l'enregistre dans le fichier `MyNFT.json`. Pour l'utiliser, nous devrons analyser le contenu en ajoutant les lignes de code suivantes à notre fichier `mint-nft.js` : ```js const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json") ``` -Si vous voulez lire l'ABI, vous pouvez l'afficher dans votre console : +Si vous voulez voir l'ABI, vous pouvez l'afficher dans votre console : ```js console.log(JSON.stringify(contract.abi)) ``` -Pour exécuter `mint-nft.js` et voir votre ABI affichée dans la console, naviguez vers votre terminal et exécutez : +Pour exécuter `mint-nft.js` et voir votre ABI s'afficher dans la console, allez dans votre terminal et exécutez : ```js node scripts/mint-nft.js ``` -## Étape 4 : Configurer les métadonnées de votre NFT en utilisant IPFS {#config-meta} +## Étape 4 : Configurer les métadonnées pour votre NFT à l'aide d'IPFS {#config-meta} -Si vous vous rappelez de la première partie de notre tutoriel, notre fonction de contrat intelligent `mintNFT` accepte un paramètre tokenURI qui doit se résoudre en un document JSON décrivant les métadonnées du NFT - ce qui donne vraiment vie au NFT, en lui permettant d'avoir des propriétés configurables, comme un nom, une description ou encore une image, entre autres. +Si vous vous souvenez de notre tutoriel de la partie 1, notre fonction de contrat intelligent `mintNFT` prend en entrée un paramètre tokenURI qui doit correspondre à un document JSON décrivant les métadonnées du NFT — c'est vraiment ce qui donne vie au NFT, lui permettant d'avoir des propriétés configurables, telles qu'un nom, une description, une image et d'autres attributs. -> _IPFS (système de fichiers interplanétaire) est un protocole décentralisé et un réseau pair-à-pair permettant de stocker et de partager des données au sein d'un système de fichiers distribué._ +> _Interplanetary File System (IPFS) est un protocole décentralisé et un réseau pair-à-pair pour le stockage et le partage de données dans un système de fichiers distribué._ -Nous utiliserons Pinata, une API IPFS pratique et une boîte à outils, pour stocker nos ressources et métadonnées NFT afin de nous assurer que notre NFT est véritablement décentralisée. Si vous ne possédez pas de compte Pinata, vous pouvez en créer un gratuitement [ici](https://app.pinata.cloud) puis suivre les étapes pour confirmer votre adresse e-mail. +Nous utiliserons Pinata, une API IPFS et une boîte à outils pratiques, pour stocker notre actif NFT et ses métadonnées afin de garantir que notre NFT est véritablement décentralisé. Si vous n'avez pas de compte Pinata, créez-en un gratuitement [ici](https://app.pinata.cloud) et suivez les étapes pour vérifier votre e-mail. -Une fois que vous avez créé un compte : +Une fois votre compte créé : -- Allez sur la page « Fichiers » et cliquez sur le bouton bleu « Upload » en haut à gauche de la page. +- Rendez-vous sur la page « Files » et cliquez sur le bouton bleu « Upload » en haut à gauche de la page. -- Téléchargez une image sur Pinata — ce sera l'image de votre NFT. N’hésitez pas à nommer la ressource comme vous le souhaitez +- Téléversez une image sur Pinata — ce sera l'image de votre NFT. N’hésitez pas à nommer l'actif comme vous le souhaitez -- Après le téléchargement, vous verrez les informations sur le fichier dans le tableau de la page « Fichiers ». Vous verrez également une colonne CID. Vous pouvez copier le CID en cliquant sur le bouton copier à côté de celui-ci. Vous pouvez voir votre téléchargement sur `https://gateway.pinata.cloud/ipfs/`. Vous pouvez trouver l'image que nous avons utilisée sur IPFS [ici](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5), par exemple. +- Après le téléversement, vous verrez les informations du fichier dans le tableau de la page « Files ». Vous verrez également une colonne CID. Vous pouvez copier le CID en cliquant sur le bouton de copie situé à côté. Vous pouvez voir votre téléversement à l'adresse suivante : `https://gateway.pinata.cloud/ipfs/`. Vous pouvez par exemple trouver l'image que nous avons utilisée sur IPFS [ici](https://gateway.pinata.cloud/ipfs/QmZdd5KYdCFApWn7eTZJ1qgJu18urJrP9Yh1TZcZrZxxB5). -Pour les apprenants plus visuels, les étapes ci-dessus sont résumées ici : +Pour les personnes qui préfèrent un support visuel, les étapes ci-dessus sont résumées ici : -![Comment télécharger votre image sur Pinata](./instructionsPinata.gif) +![Comment téléverser votre image sur Pinata](./instructionsPinata.gif) -Maintenant, nous allons vouloir télécharger un document de plus sur Pinata. Mais avant de le faire, nous devons le créer ! +Maintenant, nous allons téléverser un document de plus sur Pinata. Mais avant cela, nous devons le créer ! -Dans votre répertoire racine, créez un nouveau fichier appelé `nft-metadata.json` et ajoutez le code json suivant : +Dans votre répertoire racine, créez un nouveau fichier nommé `nft-metadata.json` et ajoutez-y le code JSON suivant : ```json { "attributes": [ { - "trait_type": "Breed", + "trait_type": "Race", "value": "Maltipoo" }, { - "trait_type": "Eye color", - "value": "Mocha" + "trait_type": "Couleur des yeux", + "value": "Moka" } ], - "description": "The world's most adorable and sensitive pup.", + "description": "Le chiot le plus adorable et le plus sensible du monde.", "image": "ipfs://QmWmvTJmJU3pozR9ZHFmQC2DNDwi2XJtf3QGyYiiagFSWb", "name": "Ramses" } ``` -N'hésitez pas à modifier les données dans le json. Vous pouvez supprimer ou étoffer la section des attributs. Avant tout, assurez-vous que le champ image pointe vers l'emplacement de votre image IPFS — sinon, votre NFT inclura la photo d'un (adorable !) chien. +N'hésitez pas à modifier les données dans le fichier JSON. Vous pouvez supprimer ou ajouter des éléments dans la section des attributs. Plus important encore, assurez-vous que le champ de l'image pointe vers l'emplacement de votre image IPFS — sinon, votre NFT inclura une photo d'un (très mignon !) chien. -Une fois que vous avez fini de modifier le fichier JSON, enregistrez les modifications et téléversez-le sur Pinata, en suivant les mêmes étapes que précédemment, pour l'image. +Une fois que vous avez fini de modifier le fichier JSON, enregistrez-le et téléversez-le sur Pinata, en suivant les mêmes étapes que pour l'image. -![Comment télécharger votre nft-metadata.json sur Pinata](./uploadPinata.gif) +![Comment téléverser votre nft-metadata.json sur Pinata](./uploadPinata.gif) ## Étape 5 : Créer une instance de votre contrat {#instance-contract} -À présent, pour interagir avec notre contrat, nous avons besoin de l'instancier dans notre code. Pour ce faire, nous aurons besoin de l'adresse du contrat que nous pouvons obtenir à partir du déploiement ou d'[Etherscan](https://sepolia.etherscan.io/) en recherchant l'adresse que vous avez utilisée pour déployer le contrat. +Maintenant, pour interagir avec notre contrat, nous devons en créer une instance dans notre code. Pour ce faire, nous aurons besoin de l'adresse de notre contrat, que nous pouvons obtenir à partir du déploiement ou de [Blockscout](https://eth-sepolia.blockscout.com/) en consultant l'adresse que vous avez utilisée pour déployer le contrat. -![Consultez l'adresse de votre contrat sur Etherscan](./view-contract-etherscan.png) +![Afficher l'adresse de votre contrat sur Etherscan](./view-contract-etherscan.png) -Dans l'exemple ci-dessus, notre adresse de contrat est 0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778. +Dans l'exemple ci-dessus, l'adresse de notre contrat est 0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778. -Ensuite, nous utiliserons la [méthode pour contrat](https://docs.web3js.org/api/web3-eth-contract/class/Contract) Web3 pour créer notre contrat en utilisant l'ABI et l'adresse. Ajoutez ce qui suit dans votre fichier `mint-nft.js` : +Ensuite, nous utiliserons la méthode de [contrat](https://docs.web3js.org/api/web3-eth-contract/class/Contract) Web3 pour créer notre contrat à l'aide de l'ABI et de l'adresse. Dans votre fichier `mint-nft.js`, ajoutez ce qui suit : ```js const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" @@ -127,9 +131,9 @@ const nftContract = new web3.eth.Contract(contract.abi, contractAddress) ## Étape 6 : Mettre à jour le fichier `.env` {#update-env} -Maintenant, pour créer et envoyer des transactions sur la chaîne Ethereum, nous utiliserons votre adresse publique de compte Ethereum pour obtenir le nonce du compte (explication à suivre ci-dessous). +Maintenant, pour créer et envoyer des transactions vers la chaîne Ethereum, nous utiliserons l'adresse de votre compte public Ethereum pour obtenir le nonce du compte (nous l'expliquerons ci-dessous). -Ajoutez votre clé publique à votre fichier `.env` — si vous avez terminé la première partie du tutoriel, notre fichier `.env` devrait maintenant ressembler à ceci : +Ajoutez votre clé publique à votre fichier `.env` — si vous avez terminé la partie 1 du tutoriel, votre fichier `.env` devrait maintenant ressembler à ceci : ```js API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key" @@ -139,25 +143,25 @@ PUBLIC_KEY = "your-public-account-address" ## Étape 7 : Créer votre transaction {#create-txn} -En premier lieu, définissons une fonction nommée `mintNFT(tokenData)` et créons notre transaction en faisant ce qui suit : +Tout d'abord, nous allons définir une fonction nommée `mintNFT(tokenData)` et créer notre transaction en procédant comme suit : -1. Récupérez la clé privée _PRIVATE_KEY_ et la clé publique _PUBLIC_KEY_ depuis le fichier `.env`. +1. Récupérez vos `PRIVATE_KEY` et `PUBLIC_KEY` depuis le fichier `.env`. -1. Ensuite, nous devrons trouver le nonce du compte. La spécification nonce est utilisée pour garder une trace du nombre de transactions envoyées à partir de votre adresse — ce dont nous avons besoin pour des raisons de sécurité et pour empêcher [les attaques par répétition](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce). Pour obtenir le nombre de transactions envoyées à partir de votre adresse, nous utilisons [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). +2. Ensuite, nous devrons déterminer le nonce du compte. La spécification de nonce est utilisée pour suivre le nombre de transactions envoyées depuis votre adresse. Nous en avons besoin pour des raisons de sécurité et pour empêcher les [attaques par rejeu](https://docs.alchemyapi.io/resources/blockchain-glossary#account-nonce). Pour obtenir le nombre de transactions envoyées depuis votre adresse, nous utilisons [getTransactionCount](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc#eth_gettransactioncount). -1. Enfin, nous allons configurer notre transaction avec les informations suivantes : +3. Enfin, nous allons configurer notre transaction avec les informations suivantes : -- `'from': PUBLIC_KEY` — L'origine de notre transaction est notre adresse publique +- `'from': PUBLIC_KEY` — L'origine de notre transaction est notre adresse publique. -- `'to': contractAddress` — Le contrat avec lequel nous souhaitons interagir et envoyer la transaction +- `'to': contractAddress` — Le contrat avec lequel nous souhaitons interagir et auquel nous souhaitons envoyer la transaction. -- `'nonce': nonce` — Le nonce du compte avec le nombre de transactions envoyées à partir de notre adresse +- `'nonce': nonce` — Le nonce du compte, avec le nombre de transactions envoyées depuis notre adresse. -- `'gas': estimatedGas` — Le gaz nécessaire estimé pour réaliser la transaction +- `'gas': estimatedGas` — La quantité de gaz estimée nécessaire pour effectuer la transaction. -- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` — Le calcul que nous souhaitons effectuer dans cette transaction — qui, dans le cas présent, est le fait de frapper un NFT +- `'data': nftContract.methods.mintNFT(PUBLIC_KEY, md).encodeABI()` — Le calcul que nous souhaitons effectuer dans cette transaction, qui est dans ce cas le frappage d'un NFT. -Votre fichier `mint-nft.js` devrait ressembler à ceci maintenant : +Votre fichier `mint-nft.js` devrait maintenant ressembler à ceci : ```js require('dotenv').config(); @@ -173,9 +177,9 @@ Votre fichier `mint-nft.js` devrait ressembler à ceci maintenant : const nftContract = new web3.eth.Contract(contract.abi, contractAddress); async function mintNFT(tokenURI) { - const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //obtenir le dernier nonce - //the transaction + //la transaction const tx = { 'from': PUBLIC_KEY, 'to': contractAddress, @@ -188,9 +192,9 @@ Votre fichier `mint-nft.js` devrait ressembler à ceci maintenant : ## Étape 8 : Signer la transaction {#sign-txn} -Maintenant que nous avons créé notre transaction, nous devons la signer afin de l’envoyer. Nous utiliserons ici notre clé privée. +Maintenant que nous avons créé notre transaction, nous devons la signer pour pouvoir l'envoyer. C'est ici que nous allons utiliser notre clé privée. -`web3.eth.sendSignedTransaction` nous donnera le hachage de la transaction que nous pouvons utiliser pour nous assurer que notre transaction a été minée et n'a pas été rejetée par le réseau. Vous remarquerez dans la section de signature de la transaction que nous avons ajouté quelques vérifications d'erreurs afin de savoir si notre transaction a bien été exécutée. +`web3.eth.sendSignedTransaction` nous donnera le hachage de la transaction, que nous pourrons utiliser pour nous assurer que notre transaction a été minée et n'a pas été abandonnée par le réseau. Vous remarquerez que dans la section de signature de la transaction, nous avons ajouté une vérification des erreurs afin de savoir si notre transaction a été effectuée avec succès. ```js require("dotenv").config() @@ -206,9 +210,9 @@ const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" const nftContract = new web3.eth.Contract(contract.abi, contractAddress) async function mintNFT(tokenURI) { - const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //obtenir le dernier nonce - //the transaction + //la transaction const tx = { from: PUBLIC_KEY, to: contractAddress, @@ -225,13 +229,13 @@ async function mintNFT(tokenURI) { function (err, hash) { if (!err) { console.log( - "The hash of your transaction is: ", + "Le hachage de votre transaction est : ", hash, - "\nCheck Alchemy's Mempool to view the status of your transaction!" + "\nConsultez le Mempool d'Alchemy pour voir l'état de votre transaction !" ) } else { console.log( - "Something went wrong when submitting your transaction:", + "Un problème est survenu lors de l'envoi de votre transaction :", err ) } @@ -239,22 +243,22 @@ async function mintNFT(tokenURI) { ) }) .catch((err) => { - console.log(" Promise failed:", err) + console.log(" La promesse a échoué :", err) }) } ``` -## Étape 9 : Appelez `mintNFT` et exécutez le nœud `mint-nft.js` {#call-mintnft-fn} +## Étape 9 : Appeler `mintNFT` et exécuter `node mint-nft.js` {#call-mintnft-fn} -Vous vous souvenez du `metadata.json` que vous avez téléchargé sur Pinata ? Récupérez son code de hachage et passez-le comme paramètre à la fonction `mintNFT` `https://gateway.pinata.cloud/ipfs/` +Vous vous souvenez du fichier `metadata.json` que vous avez téléversé sur Pinata ? Obtenez son code de hachage sur Pinata et transmettez ce qui suit comme paramètre à la fonction `mintNFT` : `https://gateway.pinata.cloud/ipfs/` Voici comment obtenir le code de hachage : -![Comment obtenir votre hashcode de métadonnées nft sur Pinata](./metadataPinata.gif)_Comment obtenir votre code de hachage de métadonnées nft sur Pinata_ +![Comment obtenir le code de hachage des métadonnées de votre NFT sur Pinata](./metadataPinata.gif)_Comment obtenir le code de hachage des métadonnées de votre NFT sur Pinata_ -> Vérifiez que le code de hachage que vous avez copié est un lien vers votre `metadata.json` en chargeant `https://gateway.pinata.cloud/ipfs/` dans une fenêtre séparée. La page devrait ressembler à la capture d'écran ci-dessous : +> Vérifiez que le code de hachage que vous avez copié renvoie bien à votre **metadata.json** en chargeant `https://gateway.pinata.cloud/ipfs/` dans une fenêtre distincte. La page devrait ressembler à la capture d'écran ci-dessous : -![Votre page devrait afficher les métadonnées json](./metadataJSON.png)_Votre page devrait afficher les métadonnées json_ +![Votre page devrait afficher les métadonnées JSON](./metadataJSON.png)_Votre page devrait afficher les métadonnées JSON_ Au final, votre code devrait ressembler à ceci : @@ -272,9 +276,9 @@ const contractAddress = "0x5a738a5c5fe46a1fd5ee7dd7e38f722e2aef7778" const nftContract = new web3.eth.Contract(contract.abi, contractAddress) async function mintNFT(tokenURI) { - const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce + const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //obtenir le dernier nonce - //the transaction + //la transaction const tx = { from: PUBLIC_KEY, to: contractAddress, @@ -291,13 +295,13 @@ async function mintNFT(tokenURI) { function (err, hash) { if (!err) { console.log( - "The hash of your transaction is: ", + "Le hachage de votre transaction est : ", hash, - "\nCheck Alchemy's Mempool to view the status of your transaction!" + "\nConsultez le Mempool d'Alchemy pour voir l'état de votre transaction !" ) } else { console.log( - "Something went wrong when submitting your transaction:", + "Un problème est survenu lors de l'envoi de votre transaction :", err ) } @@ -305,7 +309,7 @@ async function mintNFT(tokenURI) { ) }) .catch((err) => { - console.log("Promise failed:", err) + console.log("La promesse a échoué :", err) }) } @@ -314,16 +318,18 @@ mintNFT("ipfs://QmYueiuRNmL4MiA2GwtVMm6ZagknXnSpQnB3z2gWbz36hP") Maintenant, exécutez `node scripts/mint-nft.js` pour déployer votre NFT. Après quelques secondes, vous devriez voir une réponse comme celle-ci dans votre terminal : - Le hachage de votre transaction est : 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8e8 + ``` + Le hachage de votre transaction est : 0x301791fdf492001fcd9d5e5b12f3aa1bbbea9a88ed24993a8ab2cdae2d06e1e8 - Vérifiez le Mempool d'Alchemy pour voir l'état de votre transaction ! + Consultez le Mempool d'Alchemy pour voir l'état de votre transaction ! + ``` -Ensuite, consultez votre [Alchemy mempool](https://dashboard.alchemyapi.io/mempool) pour voir l'état de votre transaction (en attente, minée ou rejetée par le réseau). Si votre transaction a été rejetée, il est également utile de vérifier [Sepolia Etherscan](https://sepolia.etherscan.io/) et rechercher votre hachage de transaction. +Ensuite, visitez le [mempool d'Alchemy](https://dashboard.alchemyapi.io/mempool) pour voir l'état de votre transaction (si elle est en attente, minée ou abandonnée par le réseau). Si votre transaction a été abandonnée, il est également utile de consulter [Blockscout](https://eth-sepolia.blockscout.com/) et de rechercher le hachage de votre transaction. -![Voir le hachage de votre transaction NFT sur Etherscan](./view-nft-etherscan.png)_Voir le hachage de votre transaction NFT sur Etherscan_ +![Afficher le hachage de votre transaction NFT sur Etherscan](./view-nft-etherscan.png)_Afficher le hachage de votre transaction NFT sur Etherscan_ -Et voilà ! Vous avez maintenant déployé ET frappé un NFT sur la blockchain Ethereum. +Et c'est tout ! Vous avez maintenant déployé ET frappé un NFT sur la blockchain Ethereum -En utilisant `mint-nft.js` vous pouvez frapper autant de NFT que vous (ou votre wallet crypto) désirez ! Assurez-vous juste de passer une nouvelle URI de jeton décrivant les métadonnées du NFT (sinon, vous ne réaliserez qu'une multitude de métadonnées identiques avec différents identifiants). +Avec `mint-nft.js`, vous pouvez frapper autant de NFT que votre cœur (et votre portefeuille) le désire ! Veillez simplement à transmettre une nouvelle URI de jeton décrivant les métadonnées du NFT (sinon, vous finirez par en créer un tas d'identiques avec des ID différents). -Sans doute, vous souhaiteriez pouvoir afficher votre NFT dans votre portefeuille — alors n’oubliez pas de consulter la [Partie 3 : Comment voir votre NFT dans votre portefeuille](/developers/tutorials/how-to-view-nft-in-metamask/) ! +Vous aimeriez probablement pouvoir montrer votre NFT dans votre portefeuille — alors ne manquez pas de consulter la [Partie 3 : Comment afficher votre NFT dans votre portefeuille](/developers/tutorials/how-to-view-nft-in-metamask/) ! diff --git a/public/content/translations/fr/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md b/public/content/translations/fr/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md index a18795c45ad..407775c31a0 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-mock-solidity-contracts-for-testing/index.md @@ -4,64 +4,32 @@ description: Pourquoi vous devriez vous amuser avec vos contrats lors de vos tes author: Markus Waas lang: fr tags: - - "solidity" - - "contrats intelligents" - - "test" - - "bouchonnage" + [ + "solidité", + "contrats intelligents", + "test", + "simulation" + ] skill: intermediate published: 2020-05-02 source: soliditydeveloper.com sourceUrl: https://soliditydeveloper.com/mocking-contracts --- -[Les objets simulés](https://wikipedia.org/wiki/Mock_object) sont un modèle de conception commun en programmation orientée objet. Provenant du vieux mot français "mocquer", qui signifiait "se moquer de", sa signification a évolué en "imiter quelque chose de réel", ce qui est en fait ce que nous faisons en programmation. Vous pouvez vous moquer de vos contrats intelligents si vous le souhaitez, mais simulez-les dès que vous le pouvez. Cela vous facilite la vie. +[Les objets simulés](https://wikipedia.org/wiki/Mock_object) sont un modèle de conception courant dans la programmation orientée objet. Provenant du vieux mot français "mocquer", qui signifiait "se moquer de", sa signification a évolué en "imiter quelque chose de réel", ce qui est en fait ce que nous faisons en programmation. Vous pouvez vous moquer de vos contrats intelligents si vous le souhaitez, mais simulez-les dès que vous le pouvez. Cela vous facilite la vie. -## Contrats de test unitaire avec simulation {#unit-testing-contracts-with-mocks} +## Test unitaire de contrats avec des objets simulés {#unit-testing-contracts-with-mocks} -Simuler un contrat signifie essentiellement créer une seconde version de ce contrat qui se comporte d'une manière très similaire à la version originale, mais qui peut être facilement contrôlé par le développeur. Vous vous retrouvez souvent avec des contrats complexes où vous ne voulez que [tester de petites parties du contrat](/developers/docs/smart-contracts/testing/). Le problème est le suivant : que se passe-t-il si le test de cette petite partie exige un état de contrat très spécifique dans lequel il est difficile de s'y retrouver ? +Simuler un contrat signifie essentiellement créer une seconde version de ce contrat qui se comporte d'une manière très similaire à la version originale, mais qui peut être facilement contrôlé par le développeur. Vous vous retrouvez souvent avec des contrats complexes où vous voulez seulement [tester unitairement de petites parties du contrat](/developers/docs/smart-contracts/testing/). Le problème est le suivant : que se passe-t-il si le test de cette petite partie exige un état de contrat très spécifique dans lequel il est difficile de s'y retrouver ? Vous pouvez écrire une logique de configuration de test complexe à chaque fois que le contrat est dans l'état requis ou vous pouvez écrire une simulation. Il est facile de simuler un contrat en utilisant l'héritage. Il suffit de créer un second contrat fictif qui hérite du contrat original. Vous pouvez maintenant remplacer les fonctions sur votre contrat fictif. Voyons cela avec un exemple. ## Exemple : ERC20 privé {#example-private-erc20} -Notre exemple est celui d'un contrat ERC-20 ayant une durée de vie privée initiale. Le propriétaire peut gérer les utilisateurs privés et seuls ces derniers seront autorisés à recevoir des jetons au début. Une fois un certain temps écoulé, tout le monde sera autorisé à utiliser les jetons. Si vous êtes curieux, sachez que nous utilisons le crochet [`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks) des nouveaux contrats OpenZeppelin v3. +Notre exemple est celui d'un contrat ERC-20 ayant une durée de vie privée initiale. Le propriétaire peut gérer les utilisateurs privés et seuls ces derniers seront autorisés à recevoir des jetons au début. Une fois un certain temps écoulé, tout le monde sera autorisé à utiliser les jetons. Si vous êtes curieux, nous utilisons le hook [`_beforeTokenTransfer`](https://docs.openzeppelin.com/contracts/5.x/extending-contracts#using-hooks) des nouveaux contrats OpenZeppelin v3. ```solidity -pragma solidity ^0.6.0; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; - -contract PrivateERC20 is ERC20, Ownable { - mapping (address => bool) public isPrivateUser; - uint256 private publicAfterTime; - - constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public { - publicAfterTime = now + privateERC20timeInSec; - } - - function addUser(address user) external onlyOwner { - isPrivateUser[user] = true; - } - - function isPublic() public view returns (bool) { - return now >= publicAfterTime; - } - - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { - super._beforeTokenTransfer(from, to, amount); - - require(_validRecipient(to), "PrivateERC20: invalid recipient"); - } - - function _validRecipient(address to) private view returns (bool) { - if (isPublic()) { - return true; - } - - return isPrivateUser[to]; - } -} +pragma solidity ^0.6.0;\n\nimport "@openzeppelin/contracts/token/ERC20/ERC20.sol";\nimport "@openzeppelin/contracts/access/Ownable.sol";\n\ncontract PrivateERC20 is ERC20, Ownable {\n mapping (address => bool) public isPrivateUser;\n uint256 private publicAfterTime;\n\n constructor(uint256 privateERC20timeInSec) ERC20("PrivateERC20", "PRIV") public {\n publicAfterTime = now + privateERC20timeInSec;\n }\n\n function addUser(address user) external onlyOwner {\n isPrivateUser[user] = true;\n }\n\n function isPublic() public view returns (bool) {\n return now >= publicAfterTime;\n }\n\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {\n super._beforeTokenTransfer(from, to, amount);\n\n require(_validRecipient(to), "PrivateERC20: invalid recipient");\n }\n\n function _validRecipient(address to) private view returns (bool) {\n if (isPublic()) {\n return true;\n }\n\n return isPrivateUser[to];\n }\n} ``` Nous allons maintenant en créer une version fictive. @@ -87,20 +55,20 @@ contract PrivateERC20Mock is PrivateERC20 { Vous obtiendrez l'un des messages d'erreur suivants : -- `PrivateERC20Mock.sol: TypeError: Overriding function is missing "override" specifier.` -- `PrivateERC20.sol: TypeError: Trying to override non-virtual function. Did you forget to add "virtual"?.` +- `PrivateERC20Mock.sol: TypeError: La fonction de remplacement ne comporte pas de spécificateur « override ».' +- `PrivateERC20.sol : TypeError : Tentative de remplacement d'une fonction non virtuelle. Avez-vous oublié d'ajouter « virtual » ?. ' -Comme nous utilisons la nouvelle version 0.6 de Solidity, nous devons ajouter le mot clé `virtual` pour les fonctions qui peuvent être remplacées et remplacer pour la fonction de remplacement. Alors ajoutons-les aux deux fonctions `isPublic`. +Puisque nous utilisons la nouvelle version 0.6 de Solidity, nous devons ajouter le mot-clé `virtual` pour les fonctions qui peuvent être surchargées, et `override` pour la fonction qui surcharge. Ajoutons-les donc aux deux fonctions `isPublic`. -Dans vos tests unitaires, vous pouvez désormais utiliser `PrivateERC20Mock` à la place. Pour tester le comportement pendant le temps d'utilisation privée, utilisez `setIsPublic(false)` et, de la même manière, `setIsPublic(true)` pour tester le temps d'utilisation publique. Bien sûr, dans notre exemple, nous pourrions juste utiliser [des aides de temps](https://docs.openzeppelin.com/test-helpers/0.5/api#increase) pour également changer les temps correspondants. Mais l'utilisation d'une version fictive devrait désormais être plus claire. Vous pouvez imaginer des scénarios où il n'est pas aussi simple de faire avancer le temps. +Maintenant, dans vos tests unitaires, vous pouvez utiliser `PrivateERC20Mock` à la place. Lorsque vous voulez tester le comportement pendant la période d'utilisation privée, utilisez `setIsPublic(false)` et de même `setIsPublic(true)` pour tester la période d'utilisation publique. Bien sûr, dans notre exemple, nous pourrions également utiliser des [utilitaires de temps](https://docs.openzeppelin.com/test-helpers/0.5/api#increase) pour modifier les temps en conséquence. Mais l'utilisation d'une version fictive devrait désormais être plus claire. Vous pouvez imaginer des scénarios où il n'est pas aussi simple de faire avancer le temps. -## Simuler de nombreux contrats {#mocking-many-contracts} +## Simulation de nombreux contrats {#mocking-many-contracts} -La situation peut devenir confuse si vous devez créer un autre contrat pour chaque simulation. Si cela vous dérange, vous pouvez jeter un coup d'oeil à la bibliothèque [MockContract](https://github.com/gnosis/mock-contract). Elle vous permet de remplacer et de modifier les comportements des contrats à la volée. Cependant, cela ne fonctionne que pour simuler des appels à un autre contrat, cela ne fonctionnerait donc pas dans notre exemple. +La situation peut devenir confuse si vous devez créer un autre contrat pour chaque simulation. Si cela vous dérange, vous pouvez jeter un œil à la bibliothèque [MockContract](https://github.com/gnosis/mock-contract). Elle vous permet de surcharger et de modifier les comportements des contrats à la volée. Cependant, cela ne fonctionne que pour simuler des appels à un autre contrat, cela ne fonctionnerait donc pas dans notre exemple. ## La simulation peut être encore plus puissante {#mocking-can-be-even-more-powerful} Les pouvoirs de la simulation ne s'arrêtent pas là. -- Ajout de fonctions : Il peut être utile non seulement de remplacer une fonction spécifique, mais aussi d'ajouter des fonctions supplémentaires. Pour les jetons, un bon exemple est simplement d'avoir une fonction `mint` supplémentaire pour permettre à tout utilisateur d'obtenir de nouveaux jetons gratuitement. +- Ajout de fonctions : Il peut être utile non seulement de remplacer une fonction spécifique, mais aussi d'ajouter des fonctions supplémentaires. Un bon exemple pour les jetons consiste simplement à disposer d'une fonction `mint` supplémentaire pour permettre à n'importe quel utilisateur d'obtenir gratuitement de nouveaux jetons. - Utilisation dans les réseaux de test : Lorsque vous déployez et testez vos contrats sur des réseaux de test avec votre dapp, envisagez d'utiliser une version fictive. Évitez de remplacer des fonctions à moins que cela ne soit indispensable. Après tout, vous voulez tester la logique réelle. Il peut néanmoins être utile d'ajouter, par exemple, une fonction de réinitialisation, celle-ci vous permettant de réinitialiser simplement l'état du contrat au début, sans avoir à effectuer de nouveau déploiement. Évidemment, vous ne voudriez pas de cela dans un contrat de réseau principal. diff --git a/public/content/translations/fr/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md b/public/content/translations/fr/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md index 48ff428c35d..30240e30358 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/index.md @@ -4,20 +4,22 @@ description: Comment utiliser Echidna pour tester automatiquement les contrats i author: "Trailofbits" lang: fr tags: - - "solidity" - - "contrats intelligents" - - "sécurité" - - "test" - - "fuzzing" + [ + "solidité", + "contrats intelligents", + "sécurité", + "test", + "fuzzing" + ] skill: advanced published: 2020-04-10 -source: Créer des contrats sécurisés +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/echidna --- ## Installation {#installation} -Echidna peut être installé via Docker ou en utilisant un binaire pré-compilé. +Echidna peut être installé via Docker ou en utilisant un binaire précompilé. ### Echidna via Docker {#echidna-through-docker} @@ -26,9 +28,9 @@ docker pull trailofbits/eth-security-toolbox docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox ``` -_La dernière commande exécute eth-security-toolbox dans un docker qui a accès à votre répertoire actuel. Vous pouvez changer les fichiers depuis votre invite, et exécuter les outils sur les fichiers depuis le docker_ +_La dernière commande exécute eth-security-toolbox dans un docker qui a accès à votre répertoire actuel. Vous pouvez modifier les fichiers depuis votre hôte, et exécuter les outils sur les fichiers depuis le docker_ -Dans docker, exécutez : +Dans Docker, exécutez : ```bash solc-select 0.5.11 @@ -41,27 +43,27 @@ cd /home/training ## Introduction au fuzzing basé sur les propriétés {#introduction-to-property-based-fuzzing} -Echidna est un fuzzer basé sur les propriétés que nous avons décrit dans nos blogs précédents ([1](https://blog.trailofbits.com/2018/03/09/echidna-a-smart-fuzzer-for-ethereum/), [2](https://blog.trailofbits.com/2018/05/03/state-machine-testing-with-echidna/), [3](https://blog.trailofbits.com/2020/03/30/an-echidna-for-all-seasons/)). +Echidna est un fuzzer basé sur les propriétés, que nous avons décrit dans nos précédents articles de blog ([1](https://blog.trailofbits.com/2018/03/09/echidna-a-smart-fuzzer-for-ethereum/), [2](https://blog.trailofbits.com/2018/05/03/state-machine-testing-with-echidna/), [3](https://blog.trailofbits.com/2020/03/30/an-echidna-for-all-seasons/)). -### Fuzzing (test à données aléatoires) {#fuzzing} +### Fuzzing {#fuzzing} -Le [Fuzzing](https://wikipedia.org/wiki/Fuzzing) est une technique bien connue dans la communauté concernée par la sécurité. Il consiste à générer des entrées plus ou moins aléatoires pour trouver des bogues dans le programme. Les fuzzers pour les logiciels traditionnels (comme [AFL](http://lcamtuf.coredump.cx/afl/) ou [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)) sont connus pour être des outils efficaces quant au repérage des bogues. +[Le fuzzing](https://wikipedia.org/wiki/Fuzzing) est une technique bien connue dans la communauté de la sécurité. Elle consiste à générer des entrées plus ou moins aléatoires pour trouver des bogues dans le programme. Les fuzzers pour les logiciels traditionnels (tels que [AFL](http://lcamtuf.coredump.cx/afl/) ou [LibFuzzer](https://llvm.org/docs/LibFuzzer.html)) sont connus pour être des outils efficaces pour trouver des bogues. -Au-delà de la génération purement aléatoire d'entrées, il existe de nombreuse techniques et stratégies pour générer de bonnes contributions, y compris : +Au-delà de la génération purement aléatoire d'entrées, il existe de nombreuses techniques et stratégies pour générer de bonnes entrées, notamment : -- Obtenez des commentaires en retour de chaque exécution et la génération de guides en les utilisant. Par exemple, si une entrée nouvellement générée mène à la découverte d'un nouveau chemin, il peut y avoir un sens à générer de nouvelles entrées s'en rapprochant. -- Génération d'entrée dans le respect d'une contrainte structurelle. Par exemple, si votre entrée contient un en-tête avec une somme de contrôle, il sera logique de laisser le fuzzer générer une entrée validant la somme de contrôle. -- Utiliser des entrées connues pour générer de nouvelles entrées : si vous avez accès à un grand jeu de données d'entrée valide, votre fuzzer peut générer de nouvelles entrées à partir d'elles, plutôt que de faire partir de zéro sa génération. Elles sont généralement appelées _seeds_. +- Obtenir des retours de chaque exécution et les utiliser pour guider la génération. Par exemple, si une nouvelle entrée générée mène à la découverte d'un nouveau chemin, il peut être judicieux de générer de nouvelles entrées proches de celle-ci. +- Générer l'entrée en respectant une contrainte structurelle. Par exemple, si votre entrée contient un en-tête avec une somme de contrôle, il sera logique de laisser le fuzzer générer une entrée validant la somme de contrôle. +- Utiliser des entrées connues pour en générer de nouvelles : si vous avez accès à un grand jeu de données d'entrées valides, votre fuzzer peut générer de nouvelles entrées à partir de celles-ci, plutôt que de commencer la génération à partir de zéro. On les appelle généralement des _seeds_. -### Fuzzing orienté propriétés {#property-based-fuzzing} +### Fuzzing basé sur les propriétés {#property-based-fuzzing} -Echidna appartient à une famille spécifique de fuzzer : le fuzzing basé sur des propriétés fortement inspirées par [QuickCheck](https://wikipedia.org/wiki/QuickCheck). Contrairement au fuzzer classique qui va essayer de trouver des plantages, Echidna essayera de casser les invariants définis par l'utilisateur. +Echidna appartient à une famille spécifique de fuzzer : le fuzzing basé sur les propriétés, fortement inspiré de [QuickCheck](https://wikipedia.org/wiki/QuickCheck). Contrairement à un fuzzer classique qui essaiera de trouver des plantages, Echidna essaiera de rompre des invariants définis par l'utilisateur. -Dans les contrats intelligents, les invariants sont des fonctions Solidity qui peuvent représenter tout état incorrect ou non valide que le contrat peut atteindre, y compris : +Dans les contrats intelligents, les invariants sont des fonctions Solidity, qui peuvent représenter n'importe quel état incorrect ou invalide que le contrat peut atteindre, notamment : - Contrôle d'accès incorrect : l'attaquant est devenu le propriétaire du contrat. -- Machine d'état incorrecte : les jetons peuvent être transférés pendant que le contrat est suspendu. -- Arithmétique incorrecte : l'utilisateur peut faire déborder son solde et obtenir des jetons gratuits en illimités. +- Machine d'état incorrecte : les jetons peuvent être transférés alors que le contrat est en pause. +- Arithmétique incorrecte : l'utilisateur peut provoquer un underflow de son solde et obtenir un nombre illimité de jetons gratuits. ### Tester une propriété avec Echidna {#testing-a-property-with-echidna} @@ -83,7 +85,7 @@ contract Token{ } ``` -Nous prendrons comme hypothèse que ce jeton doit avoir les propriétés suivantes : +Nous supposerons que ce jeton doit avoir les propriétés suivantes : - N'importe qui peut avoir au maximum 1000 jetons - Le jeton ne peut pas être transféré (ce n'est pas un jeton ERC20) @@ -92,17 +94,17 @@ Nous prendrons comme hypothèse que ce jeton doit avoir les propriétés suivant Les propriétés Echidna sont des fonctions Solidity. Une propriété doit : -- Ne contenir aucun argument -- Renvoyer `true` si elle a réussi -- Avoir son nom commençant par `echidna` +- Ne pas avoir d'argument +- Renvoyer `true` si elle réussit +- Avoir un nom qui commence par `echidna` -Echidna va : +Echidna : - Générer automatiquement des transactions arbitraires pour tester la propriété. -- Signaler toute transaction menant à une propriété pour renvoyer `false` ou retourner une erreur. -- Ignorer l'effet secondaire lors de l'appel d'une propriété (c'est-à-dire si la propriété change une variable d'état, elle est rejetée après le test) +- Signaler toute transaction qui amène une propriété à renvoyer `false` ou à lever une erreur. +- Ignorer les effets de bord lors de l'appel d'une propriété (c.-à-d. que si la propriété modifie une variable d'état, la modification est annulée après le test) -La propriété suivante vérifie que l'appelant ne dispose pas de plus de 1000 jetons : +La propriété suivante vérifie que l'appelant n'a pas plus de 1000 jetons : ```solidity function echidna_balance_under_1000() public view returns(bool){ @@ -120,18 +122,18 @@ contract TestToken is Token{ } ``` -[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol) implémente la propriété et hérite du jeton. +[`token.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/token.sol) met en œuvre la propriété et hérite du jeton. -### Démarrer un contrat {#initiate-a-contract} +### Initialiser un contrat {#initiate-a-contract} -Echidna a besoin d'un [constructeur](/developers/docs/smart-contracts/anatomy/#constructor-functions) sans argument. Si votre contrat nécessite une initialisation spécifique, vous devez le faire dans le constructeur. +Echidna a besoin d'un [constructeur](/developers/docs/smart-contracts/anatomy/#constructor-functions) sans argument. Si votre contrat nécessite une initialisation spécifique, vous devez la faire dans le constructeur. -Il existe quelques adresses spécifiques dans Echidna : +Il y a quelques adresses spécifiques dans Echidna : -- `0x00a329c0648769A73afAc7F9381E08FB43dBEA7` qui appelle le constructeur. -- `0x10000`, `0x20000`, et `0x00a329C0648769a73afAC7F9381e08fb43DBEA70` qui appellent aléatoirement les autres fonctions. +- `0x00a329c0648769A73afAc7F9381E08FB43dBEA72` qui appelle le constructeur. +- `0x10000`, `0x20000` et `0x00a329C0648769a73afAC7F9381e08fb43DBEA70` qui appellent aléatoirement les autres fonctions. -Nous n'avons pas besoin d'initialisation particulière dans notre exemple actuel, de fait, notre constructeur est vide. +Nous n'avons pas besoin d'initialisation particulière dans notre exemple actuel, par conséquent notre constructeur est vide. ### Exécuter Echidna {#run-echidna} @@ -149,7 +151,7 @@ echidna-test contract.sol --contract MyContract ### Résumé : Tester une propriété {#summary-testing-a-property} -Ce qui suit permet de résumer le lancement d'Echidna pour notre exemple : +Ce qui suit résume l'exécution d'Echidna sur notre exemple : ```solidity contract TestToken is Token{ @@ -172,11 +174,12 @@ echidna_balance_under_1000: failed!💥 ... ``` -Echidna a trouvé que la propriété sera compromise si `backdoor` est appelé. +Echidna a trouvé que la propriété est violée si `backdoor` est appelée. -## Fonctions de filtrage à appeler lors d'une campagne de fuzzing {#filtering-functions-to-call-during-a-fuzzing-campaign} +## Filtrer les fonctions à appeler pendant une campagne de fuzzing {#filtering-functions-to-call-during-a-fuzzing-campaign} -Nous verrons comment filtrer les fonctions à fuzzer. La cible est le contrat intelligent suivant : +Nous verrons comment filtrer les fonctions à fuzzer. +La cible est le contrat intelligent suivant : ```solidity contract C { @@ -227,7 +230,9 @@ contract C { } ``` -Ce petit exemple oblige Echidna à trouver une certaine séquence de transactions pour modifier une variable d'état. C'est une opération compliquée pour un fuzzer (il est recommandé d'utiliser un outil d'exécution symbolique comme [Manticore](https://github.com/trailofbits/manticore)). Nous pouvons exécuter Echidna pour vérifier ceci : +Ce petit exemple force Echidna à trouver une certaine séquence de transactions pour modifier une variable d'état. +C'est difficile pour un fuzzer (il est recommandé d'utiliser un outil d'exécution symbolique comme [Manticore](https://github.com/trailofbits/manticore)). +Nous pouvons exécuter Echidna pour vérifier cela : ```bash echidna-test multi.sol @@ -236,30 +241,32 @@ echidna_state4: passed! 🎉 Seed: -3684648582249875403 ``` -### Fonctions de filtrage {#filtering-functions} +### Filtrage des fonctions {#filtering-functions} -Echidna a du mal à trouver la séquence correcte pour tester ce contrat, car les deux fonctions de réinitialisation (`reset1` et `reset2`) mettront toutes les variables d'état sur `false`. Cependant, nous pouvons utiliser une fonctionnalité spéciale d'Echidna pour mettre sur liste noire la fonction reset ou pour ne mettre sur liste blanche que les fonctions `f`, `g`, `h` et `i`. +Echidna a du mal à trouver la séquence correcte pour tester ce contrat, car les deux fonctions de réinitialisation (`reset1` et `reset2`) mettront toutes les variables d'état à `false`. +Cependant, nous pouvons utiliser une fonctionnalité spéciale d'Echidna pour soit mettre sur liste noire la fonction de réinitialisation, soit mettre sur liste blanche uniquement les fonctions `f`, `g`, +`h` et `i`. -Pour bloquer les fonctions, nous pouvons utiliser ce fichier de configuration : +Pour mettre des fonctions sur liste noire, nous pouvons utiliser ce fichier de configuration : ```yaml filterBlacklist: true filterFunctions: ["reset1", "reset2"] ``` -Une autre approche des fonctions de filtrage est de lister les fonctions dans une liste blanche. Pour cela, nous pouvons utiliser ce fichier de configuration : +Une autre approche pour filtrer les fonctions consiste à lister les fonctions sur liste blanche. Pour ce faire, nous pouvons utiliser ce fichier de configuration : ```yaml filterBlacklist: false filterFunctions: ["f", "g", "h", "i"] ``` -- `filterBlacklist` est sur `true` par défaut. -- Le filtrage sera effectué uniquement par le nom (sans les paramètres). Si vous avez `f()` et `f(uint256)`, le filtre `"f"` correspondra aux deux fonctions. +- `filterBlacklist` est `true` par défaut. +- Le filtrage sera effectué uniquement par nom (sans les paramètres). Si vous avez `f()` et `f(uint256)`, le filtre `"f"` correspondra aux deux fonctions. ### Exécuter Echidna {#run-echidna-1} -Exécuter Echidna avec un fichier de configuration `blacklist.yaml` : +Pour exécuter Echidna avec un fichier de configuration `blacklist.yaml` : ```bash echidna-test multi.sol --config blacklist.yaml @@ -272,11 +279,11 @@ echidna_state4: failed!💥 i() ``` -Echidna trouvera presque immédiatement la séquence des transactions pour falsifier la propriété. +Echidna trouvera la séquence de transactions pour falsifier la propriété presque immédiatement. -### Résumé : fonctions de filtrage {#summary-filtering-functions} +### Résumé : Filtrage des fonctions {#summary-filtering-functions} -Echidna peut soit mettre sur liste noire, soit mettre sur liste blanche des fonctions à appeler pendant une campagne de fuzzing en utilisant : +Echidna peut soit mettre sur liste noire, soit mettre sur liste blanche les fonctions à appeler pendant une campagne de fuzzing en utilisant : ```yaml filterBlacklist: true @@ -288,11 +295,11 @@ echidna-test contract.sol --config config.yaml ... ``` -Echidna débute une campagne de fuzzing soit en ajoutant `f1`, `f2` et `f3` sur la liste noire, ou en les appelant uniquement selon la valeur booléenne définie dans `filterBlacklist`. +Echidna lance une campagne de fuzzing soit en mettant sur liste noire `f1`, `f2` et `f3`, soit en appelant uniquement celles-ci, en fonction de la valeur du booléen `filterBlacklist`. -## Comment tester les assertions de Solidity avec Echidna {#how-to-test-soliditys-assert-with-echidna} +## Comment tester l'assertion de Solidity avec Echidna {#how-to-test-soliditys-assert-with-echidna} -Dans ce court tutoriel, nous allons montrer comment utiliser Echidna pour tester la vérification des assertions dans les contrats. Supposons que nous ayons un contrat comme celui-ci : +Dans ce court tutoriel, nous allons montrer comment utiliser Echidna pour tester la vérification d'assertions dans les contrats. Supposons que nous ayons un contrat comme celui-ci : ```solidity contract Incrementor { @@ -309,7 +316,8 @@ contract Incrementor { ### Écrire une assertion {#write-an-assertion} -Nous voulons nous assurer que `tmp` est inférieur ou égal à `counter` après avoir retourné sa différence. Nous pourrions écrire une propriété Echidna, mais nous devrons stocker la valeur `tmp` quelque part. Au lieu de cela, nous pourrions utiliser une assertion comme celle-ci : +Nous voulons nous assurer que `tmp` est inférieur ou égal à `counter` après avoir renvoyé leur différence. Nous pourrions écrire une propriété +Echidna, mais nous devrons stocker la valeur de `tmp` quelque part. À la place, nous pourrions utiliser une assertion comme celle-ci : ```solidity contract Incrementor { @@ -326,13 +334,13 @@ contract Incrementor { ### Exécuter Echidna {#run-echidna-2} -Pour activer le test d'échec à l'assertion, créez un [fichier de configuration Echidna](https://github.com/crytic/echidna/wiki/Config) `config.yaml` : +Pour activer le test d'échec d'assertion, créez un [fichier de configuration Echidna](https://github.com/crytic/echidna/wiki/Config) `config.yaml` : ```yaml checkAsserts: true ``` -Lorsque nous exécutons ce contrat sur Echidna, nous obtenons les résultats escomptés : +Lorsque nous exécutons ce contrat dans Echidna, nous obtenons les résultats attendus : ```bash echidna-test assert.sol --config config.yaml @@ -346,15 +354,15 @@ assertion in inc: failed!💥 Seed: 1806480648350826486 ``` -Comme vous pouvez le voir, Echidna signale un échec d'assertion dans la fonction `inc`. L'ajout de plus d'une assertion par fonction est possible, mais Echidna ne peut pas dire quelle assertion a échoué. +Comme vous pouvez le voir, Echidna signale un échec d'assertion dans la fonction `inc`. Il est possible d'ajouter plus d'une assertion par fonction, mais Echidna ne peut pas dire quelle assertion a échoué. ### Quand et comment utiliser les assertions {#when-and-how-use-assertions} -Les assertions peuvent être utilisées comme alternatives aux propriétés explicites, spécialement si les conditions à vérifier sont directement liées à l'utilisation correcte de certaines opérations `f`. Ajouter des assertions après certains codes garantira que la vérification se produira immédiatement après son exécution : +Les assertions peuvent être utilisées comme alternatives aux propriétés explicites, surtout si les conditions à vérifier sont directement liées à l'utilisation correcte d'une opération `f`. L'ajout d'assertions après un morceau de code garantira que la vérification aura lieu immédiatement après son exécution : ```solidity function f(..) public { - // some complex code + // du code complexe ... assert (condition); ... @@ -362,7 +370,7 @@ function f(..) public { ``` -Au contraire, utiliser une propriété Echidna explicite va exécuter aléatoirement des transactions et il n'y a pas de moyen facile de forcer le moment exact où elle sera vérifiée. Il est toujours possible de faire ce contournement : +Au contraire, l'utilisation d'une propriété Echidna explicite exécutera des transactions de manière aléatoire et il n'y a aucun moyen simple de forcer le moment exact où elle sera vérifiée. Il est toujours possible d'utiliser cette solution de contournement : ```solidity function echidna_assert_after_f() public returns (bool) { @@ -371,22 +379,22 @@ function echidna_assert_after_f() public returns (bool) { } ``` -Cependant, il existe quelques problèmes : +Cependant, il y a quelques problèmes : -- Il échoue si `f` est déclaré comme `internal` ou `external`. -- Il n'est pas clair de savoir quels arguments doivent être utilisés pour appeler `f`. -- Si `f` s'annule, la propriété échouera. +- Elle échoue si `f` est déclarée comme `internal` ou `external`. +- On ne sait pas clairement quels arguments devraient être utilisés pour appeler `f`. +- Si `f` revert, la propriété échouera. En général, nous recommandons de suivre [la recommandation de John Regehr](https://blog.regehr.org/archives/1091) sur la façon d'utiliser les assertions : -- Ne forcez aucun effet secondaire lors de la vérification de l'assertion. Par exemple : `assert(ChangeStateAndReturn() == 1)` -- Ne revendiquez pas des déclarations évidentes. Par exemple `assert(var >= 0)` où `var` est déclaré comme `uint`. +- Ne forcez aucun effet de bord lors de la vérification de l'assertion. Par exemple : `assert(ChangeStateAndReturn() == 1)` +- Ne faites pas d'assertions sur des déclarations évidentes. Par exemple `assert(var >= 0)` où `var` est déclaré en tant que `uint`. -Enfin, s'il vous plaît **n'utilisez pas** `require` au lieu de `assert`, car dans ce cas, Echidna ne sera pas en mesure de le détecter (mais le contrat sera annulé de toute façon). +Enfin, veuillez **ne pas utiliser** `require` à la place d'`assert`, car Echidna ne sera pas en mesure de le détecter (mais le contrat sera annulé de toute façon). -### Résumé : vérification d'assertion {#summary-assertion-checking} +### Résumé : Vérification d'assertion {#summary-assertion-checking} -Ce qui suit permet de résumer le lancement d'Echidna pour notre exemple : +Ce qui suit résume l'exécution d'Echidna sur notre exemple : ```solidity contract Incrementor { @@ -413,9 +421,9 @@ assertion in inc: failed!💥 Seed: 1806480648350826486 ``` -Echidna a trouvé que l'assertion dans `inc` peut échouer si cette fonction est appelée plusieurs fois avec de grands arguments. +Echidna a découvert que l'assertion dans `inc` peut échouer si cette fonction est appelée plusieurs fois avec de grands arguments. -## Collecte et modification d'un corpus Echidna {#collecting-and-modifying-an-echidna-corpus} +## Collecter et modifier un corpus Echidna {#collecting-and-modifying-an-echidna-corpus} Nous verrons comment collecter et utiliser un corpus de transactions avec Echidna. La cible est le contrat intelligent suivant [`magic.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/example/magic.sol) : @@ -437,7 +445,9 @@ contract C { } ``` -Ce simple exemple oblige Echidna à trouver certaines valeurs pour changer une variable d'état. C'est une opération compliquée pour un fuzzer (il est recommandé d'utiliser un outil d'exécution symbolique comme [Manticore](https://github.com/trailofbits/manticore)). Nous pouvons exécuter Echidna pour vérifier ceci : +Ce simple exemple oblige Echidna à trouver certaines valeurs pour changer une variable d'état. C'est difficile pour un fuzzer +(il est recommandé d'utiliser un outil d'exécution symbolique comme [Manticore](https://github.com/trailofbits/manticore)). +Nous pouvons exécuter Echidna pour vérifier cela : ```bash echidna-test magic.sol @@ -450,7 +460,7 @@ Seed: 2221503356319272685 Cependant, nous pouvons toujours utiliser Echidna pour collecter le corpus lors de l'exécution de cette campagne de fuzzing. -### Récupération d'un corpus {#collecting-a-corpus} +### Collecte d'un corpus {#collecting-a-corpus} Pour activer la collecte de corpus, créez un répertoire de corpus : @@ -471,7 +481,8 @@ Maintenant nous pouvons exécuter notre outil et vérifier le corpus collecté : echidna-test magic.sol --config config.yaml ``` -Echidna ne peut toujours pas trouver les bonnes valeurs magiques, mais nous pouvons jeter un œil sur le corpus qu'il a collecté. Par exemple, l'un de ces fichiers était : +Echidna ne peut toujours pas trouver les bonnes valeurs magiques, mais nous pouvons jeter un œil sur le corpus qu'il a collecté. +Par exemple, l'un de ces fichiers était : ```json [ @@ -518,9 +529,10 @@ Echidna ne peut toujours pas trouver les bonnes valeurs magiques, mais nous pouv Évidemment, cette entrée ne déclenchera pas l'échec de notre propriété. Cependant, au cours de la prochaine étape, nous verrons comment la modifier en ce sens. -### Alimenter un corpus {#seeding-a-corpus} +### Amorcer un corpus {#seeding-a-corpus} -Echidna a besoin d'aide pour gérer la fonction `magic`. Nous allons copier et modifier l'entrée pour utiliser les paramètres appropriés : +Echidna a besoin d'aide pour gérer la fonction `magic`. Nous allons copier et modifier l'entrée pour utiliser des paramètres appropriés +pour elle : ```bash cp corpus/2712688662897926208.txt corpus/new.txt @@ -544,7 +556,7 @@ Seed: -7293830866560616537 Cette fois-ci, il a conclu immédiatement que la propriété a été compromise. -## Recherche de transactions à forte consommation de gaz {#finding-transactions-with-high-gas-consumption} +## Trouver les transactions à forte consommation de gaz {#finding-transactions-with-high-gas-consumption} Nous verrons comment trouver avec Echida les transactions à forte consommation de gaz. La cible est le contrat intelligent suivant : @@ -571,9 +583,10 @@ contract C { } ``` -Ici `expensive` peut avoir une grande consommation de gaz. +Ici, `expensive` peut avoir une grande consommation de gaz. -Actuellement, Echidna a toujours besoin d'une propriété pour tester : ici `echidna_test` retourne toujours `true`. Nous pouvons exécuter Echidna pour vérifier ceci : +Actuellement, Echidna a toujours besoin d'une propriété pour tester : ici, `echidna_test` renvoie toujours `true`. +Nous pouvons exécuter Echidna pour vérifier cela : ``` echidna-test gas.sol @@ -585,7 +598,7 @@ Seed: 2320549945714142710 ### Mesurer la consommation de gaz {#measuring-gas-consumption} -Pour activer avec Echidna la consommation de gaz, créez un fichier de configuration `config.yaml` : +Pour activer la consommation de gaz avec Echidna, créez un fichier de configuration `config.yaml` : ```yaml estimateGas: true @@ -598,7 +611,7 @@ seqLen: 2 estimateGas: true ``` -### Run Echidna {#run-echidna-3} +### Exécuter Echidna {#run-echidna-3} Une fois que nous avons créé le fichier de configuration, nous pouvons exécuter Echidna comme ceci : @@ -617,12 +630,14 @@ Seed: -325611019680165325 ``` -- Le gaz affiché est une estimation fournie par [HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-). +- Le gaz indiqué est une estimation fournie par [HEVM](https://github.com/dapphub/dapptools/tree/master/src/hevm#hevm-). -### Filtrer les appels de réduction de gaz {#filtering-out-gas-reducing-calls} +### Filtrer les appels qui réduisent le gaz {#filtering-out-gas-reducing-calls} -Le tutoriel ci-dessus sur les **fonctions de filtrage à appeler lors d'une campagne de fuzzing** montre comment supprimer certaines fonctions de votre test. -Cela peut être critique pour obtenir une estimation précise de gaz. Prenons l'exemple suivant : +Le tutoriel ci-dessus sur le **filtrage des fonctions à appeler pendant une campagne de fuzzing** montre comment +supprimer certaines fonctions de vos tests. +Cela peut être essentiel pour obtenir une estimation précise du gaz. +Prenons l'exemple suivant : ```solidity contract C { @@ -662,7 +677,8 @@ clear used a maximum of 35916 gas push used a maximum of 40839 gas ``` -C'est parce que le coût dépend de la taille des `addrs` et que les appels aléatoires ont tendance à laisser le tableau presque vide. Mettre sur liste noire `pop` et `clear` nous donne de bien meilleurs résultats : +C'est parce que le coût dépend de la taille de `addrs` et que les appels aléatoires ont tendance à laisser le tableau presque vide. +Mettre sur liste noire `pop` et `clear` nous donne de bien meilleurs résultats : ```yaml filterBlacklist: true @@ -677,7 +693,7 @@ push used a maximum of 40839 gas check used a maximum of 1484472 gas ``` -### Résumé : trouver des transactions avec une forte consommation de gaz {#summary-finding-transactions-with-high-gas-consumption} +### Résumé : Trouver les transactions à forte consommation de gaz {#summary-finding-transactions-with-high-gas-consumption} Echidna peut trouver des transactions avec une forte consommation de gaz en utilisant l'option de configuration `estimateGas` : diff --git a/public/content/translations/fr/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md b/public/content/translations/fr/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md index 8190b8b8b95..1d33e1f51fd 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/index.md @@ -1,36 +1,38 @@ --- title: Comment utiliser Manticore pour trouver des bugs dans les contrats intelligents -description: Comment utiliser Manticore pour trouver automatiquement des bugs dans les smart contracts +description: Comment utiliser Manticore pour trouver automatiquement des bugs dans les contrats intelligents author: Trailofbits lang: fr tags: - - "solidity" - - "contrats intelligents" - - "sécurité" - - "test" - - "vérification formelle" + [ + "solidité", + "contrats intelligents", + "sécurité", + "test", + "vérification formelle" + ] skill: advanced published: 2020-01-13 -source: Créer des contrats sécurisés +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore --- -Le but de ce tutoriel est de démontrer comment utiliser Manticore pour trouver automatiquement des bugs dans les contrats intelligents. +Le but de ce tutoriel est de montrer comment utiliser Manticore pour trouver automatiquement des bugs dans les contrats intelligents. ## Installation {#installation} -Manticore requiert >= python 3.6. Il peut être installé via pip ou avec docker. +Manticore requiert python >= 3.6. Il peut être installé via pip ou en utilisant docker. -### Manticore via Docker {#manticore-through-docker} +### Manticore via docker {#manticore-through-docker} ```bash docker pull trailofbits/eth-security-toolbox docker run -it -v "$PWD":/home/training trailofbits/eth-security-toolbox ``` -_La dernière commande exécute eth-security-toolbox dans un docker qui a accès à votre répertoire actif. Vous pouvez changer les fichiers depuis votre invite, et exécuter les outils sur les fichiers depuis le docker_ +_La dernière commande exécute eth-security-toolbox dans un docker qui a accès à votre répertoire actuel. Vous pouvez modifier les fichiers depuis votre hôte, et exécuter les outils sur les fichiers depuis le docker_ -Dans docker, lancez : +À l'intérieur de docker, exécutez : ```bash solc-select 0.5.11 @@ -55,18 +57,18 @@ python3 script.py ## Introduction à l'exécution symbolique dynamique {#introduction-to-dynamic-symbolic-execution} -### L'exécution symbolique dynamique en quelques mots {#dynamic-symbolic-execution-in-a-nutshell} +### L'exécution symbolique dynamique en bref {#dynamic-symbolic-execution-in-a-nutshell} -L'exécution symbolique dynamique (DSE) est une technique d'analyse de programme qui explore un espace d'état avec un degré élevé de conscience sémantique. Cette technique est basée sur la découverte de « chemins de programme », représentée sous la forme de formules mathématiques appelées `prédicats de chemin`. Conceptuellement, cette technique opère sur les prédicats de chemin en deux étapes : +L'exécution symbolique dynamique (DSE) est une technique d'analyse de programme qui explore un espace d'état avec un degré élevé de conscience sémantique. Cette technique est basée sur la découverte de « chemins de programme », représentés par des formules mathématiques appelées `prédicats de chemin`. Conceptuellement, cette technique opère sur les prédicats de chemin en deux étapes : 1. Ils sont construits en utilisant des contraintes sur les entrées du programme. 2. Ils sont utilisés pour générer des entrées de programme qui entraîneront l'exécution des chemins associés. -Cette approche ne produit aucun faux positif dans le sens où tous les états de programme identifiés peuvent être déclenchés lors de l'exécution concrète. Par exemple, si l'analyse trouve un dépassement d'entier, il est garanti reproductible. +Cette approche ne produit aucun faux positif dans le sens où tous les états de programme identifiés peuvent être déclenchés lors de l'exécution concrète. Par exemple, si l'analyse trouve un dépassement d'entier, sa reproductibilité est garantie. ### Exemple de prédicat de chemin {#path-predicate-example} -Pour avoir une idée du fonctionnement du DSE, prenez l'exemple suivant : +Pour avoir un aperçu du fonctionnement du DSE, considérez l'exemple suivant : ```solidity function f(uint a){ @@ -83,17 +85,17 @@ Comme `f()` contient deux chemins, un DSE construira deux prédicats de chemin d - Chemin 1 : `a == 65` - Chemin 2 : `Not (a == 65)` -Chaque prédicat de chemin est une formule mathématique qui peut être donnée à un solveur [SMT](https://wikipedia.org/wiki/Satisfiability_modulo_theories), qui tentera de résoudre l'équation. Pour le `chemin 1`, le solveur dira que le chemin peut être exploré avec `a = 65`. Pour le `Chemin 2`, le solveur peut attribuer à `a` toute valeur autre que 65, par exemple `a = 0`. +Chaque prédicat de chemin est une formule mathématique qui peut être donnée à un [solveur SMT](https://wikipedia.org/wiki/Satisfiability_modulo_theories), qui tentera de résoudre l'équation. Pour le `Chemin 1`, le solveur dira que le chemin peut être exploré avec `a = 65`. Pour le `Chemin 2`, le solveur peut donner à `a` n'importe quelle valeur autre que 65, par exemple `a = 0`. -### Vérification des propriétés {#verifying-properties} +### Vérifier les propriétés {#verifying-properties} -Manticore permet un contrôle total sur toutes les exécutions de chaque chemin. En conséquence, il vous permet d'ajouter des contraintes arbitraires à pratiquement n'importe quoi. Ce contrôle permet la création de propriétés sur le contrat. +Manticore permet un contrôle total sur l'exécution de chaque chemin. En conséquence, il vous permet d'ajouter des contraintes arbitraires à pratiquement n'importe quoi. Ce contrôle permet la création de propriétés sur le contrat. Prenons l'exemple suivant : ```solidity function unsafe_add(uint a, uint b) returns(uint c){ - c = a + b; // pas de protections d’overflow + c = a + b; // aucune protection contre le dépassement return c; } ``` @@ -102,13 +104,13 @@ Il n'y a ici qu'un seul chemin à explorer dans la fonction : - Chemin 1 : `c = a + b` -En utilisant Manticore, vous pouvez vérifier le dépassement, et ajouter des contraintes au prédicat de chemin : +En utilisant Manticore, vous pouvez vérifier le dépassement et ajouter des contraintes au prédicat de chemin : - `c = a + b AND (c < a OR c < b)` -S'il est possible de trouver une valorisation de `a` et `b` pour laquelle le prédicat de chemin ci-dessus est réalisable, cela signifie que vous avez trouvé un dépassement. Par exemple, le solveur peut générer l'entrée `a = 10 , b = MAXUINT256`. +S'il est possible de trouver des valeurs pour `a` et `b` pour lesquelles le prédicat de chemin ci-dessus est réalisable, cela signifie que vous avez trouvé un dépassement. Par exemple, le solveur peut générer l'entrée `a = 10, b = MAXUINT256`. -Si vous envisagez une version fixe: +Si vous considérez une version corrigée : ```solidity function safe_add(uint a, uint b) returns(uint c){ @@ -119,17 +121,17 @@ function safe_add(uint a, uint b) returns(uint c){ } ``` -La formule associée au contrôle de l'overflow serait : +La formule associée à la vérification de dépassement serait : - `c = a + b AND (c >= a) AND (c=>b) AND (c < a OR c < b)` -Cette formule ne peut pas être résolue, autrement dit c’est la **preuve** que la valeur `safe_add`, `c` augmentera toujours. +Cette formule ne peut pas être résolue ; en d'autres termes, c'est une **preuve** que dans `safe_add`, `c` augmentera toujours. -DSE est un outil puissant qui peut vérifier des contraintes arbitraires sur votre code. +DSE est donc un outil puissant, qui peut vérifier des contraintes arbitraires sur votre code. -## Lancer dans Manticore {#running-under-manticore} +## Exécution sous Manticore {#running-under-manticore} -Nous allons voir comment explorer un contrat intelligent avec l'API Manticore. La cible est le contrat intelligent suivant [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): +Nous allons voir comment explorer un contrat intelligent avec l'API Manticore. La cible est le contrat intelligent suivant [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol) : ```solidity pragma solidity >=0.4.24 <0.6.0; @@ -145,13 +147,13 @@ contract Simple { ### Lancer une exploration autonome {#run-a-standalone-exploration} -Vous pouvez exécuter Manticore directement sur le contrat intelligent par la commande suivante (`project` peut être un fichier Solidity, ou un répertoire de projet) : +Vous pouvez exécuter Manticore directement sur le contrat intelligent avec la commande suivante (`project` peut être un fichier Solidity, ou un répertoire de projet) : ```bash $ manticore project ``` -Vous obtiendrez une liste des cas de tests comme par exemple (l'ordre peut changer) : +Vous obtiendrez en sortie des cas de test comme celui-ci (l'ordre peut changer) : ``` ... @@ -166,39 +168,40 @@ Vous obtiendrez une liste des cas de tests comme par exemple (l'ordre peut chang ... ``` -Sans informations supplémentaires, Manticore explorera le contrat avec de nouvelles transactions symboliques jusqu’à ce qu’il n’explore plus de nouveaux chemins dans le contrat. Manticore ne lance pas de nouvelle transactions après le premier echec (par exemple : après un revert). +Sans informations supplémentaires, Manticore explorera le contrat avec de nouvelles transactions symboliques +jusqu’à ce qu’il n’explore plus de nouveaux chemins dans le contrat. Manticore n'exécute pas de nouvelles transactions après un échec (p. ex., après une annulation). -Le type de donnée que Manticore produira comme résultat sera de type dossier `mcore_*`. Parmi d’autres, vous trouverez dans le dossier : +Manticore générera les informations dans un répertoire `mcore_*`. Vous trouverez notamment dans ce répertoire : -- `global.summary` : avertissements de couverture de code et de compilateur -- `test_XXXXX.summary`: couverture de code, dernière instruction, soldes du compte par cas de test -- `test_XXXXX.tx`: liste détaillée des transactions par cas de test +- `global.summary` : couverture et avertissements du compilateur +- `test_XXXXX.summary` : couverture, dernière instruction, soldes des comptes par cas de test +- `test_XXXXX.tx` : liste détaillée des transactions par cas de test Ici, Manticore a trouvé 7 cas de test, qui correspondent à (l'ordre des noms de fichier peut changer) : -| | Transaction 0 | Transaction 1 | Transaction 2 | Résultat | -| :------------------: | :----------------------: | :-----------------: | ------------------- | :------: | -| **test_00000000.tx** | La création des contrats | f(!=65) | f(!=65) | STOP | -| **test_00000001.tx** | Création du contrat | fonction de secours | | REVERT | -| **test_00000002.tx** | Création du contrat | | | RETOUR | -| **test_00000003.tx** | Création du contrat | f(65) | | REVERT | -| **test_00000004.tx** | Création du contrat | f(!=65) | | STOP | -| **test_00000005.tx** | Création du contrat | f(!=65) | f(65) | REVERT | -| **test_00000006.tx** | Création du contrat | f(!=65) | fonction de secours | REVERT | +| | Transaction 0 | Transaction 1 | Transaction 2 | Résultat | +| :-------------------------------------------------------: | :-----------------: | :------------------------: | -------------------------- | :------: | +| **test_00000000.tx** | Création du contrat | f(!=65) | f(!=65) | STOP | +| **test_00000001.tx** | Création du contrat | fonction de secours | | REVERT | +| **test_00000002.tx** | Création du contrat | | | RETOUR | +| **test_00000003.tx** | Création du contrat | f(65) | | REVERT | +| **test_00000004.tx** | Création du contrat | f(!=65) | | STOP | +| **test_00000005.tx** | Création du contrat | f(!=65) | f(65) | REVERT | +| **test_00000006.tx** | Création du contrat | f(!=65) | fonction de secours | REVERT | -_Le résumé d'exploration f(!=65) indique f appelé avec une valeur différente de 65._ +_Le résumé d'exploration f(!=65) indique que f est appelée avec une valeur différente de 65._ -Comme vous pouvez le constater, Manticore génère un scénario de test unique pour chaque transaction réussie ou annulée. +Comme vous pouvez le constater, Manticore génère un cas de test unique pour chaque transaction réussie ou annulée. -Utilisez le flag `--quick-mode` si vous voulez une exploration rapide du code (il désactive les détecteurs de bugs, le calcul du gaz, ...) +Utilisez l'option `--quick-mode` si vous voulez une exploration rapide du code (elle désactive les détecteurs de bugs, le calcul du gaz, etc.) ### Manipuler un contrat intelligent via l'API {#manipulate-a-smart-contract-through-the-api} -Cette section décrit comment manipuler un contrat intelligent à travers l'API Python de Manticore. Vous pouvez créer un nouveau fichier avec l'extension python `*.py` et écrivez le code nécessaire en ajoutant les commandes API (dont les bases seront décrites ci-dessous) dans ce fichier, puis exécutez-le avec la commande `$ python3 *. py`. Vous pouvez également exécuter les commandes ci-dessous directement dans la console python, pour exécuter la console en utilisant la commande `$ python3`. +Cette section décrit en détail comment manipuler un contrat intelligent via l'API Python de Manticore. Vous pouvez créer un nouveau fichier avec l'extension python `.py` et y écrire le code nécessaire en ajoutant les commandes de l'API (dont les bases sont décrites ci-dessous), puis l'exécuter avec la commande `$ python3 *.py`. Vous pouvez également exécuter les commandes ci-dessous directement dans la console Python ; pour lancer la console, utilisez la commande `$ python3`. -### Création des comptes {#creating-accounts} +### Créer des comptes {#creating-accounts} -La première chose que vous devriez faire est d'initier une nouvelle blockchain avec les commandes suivantes : +La première chose à faire est d'initialiser une nouvelle blockchain avec les commandes suivantes : ```python from manticore.ethereum import ManticoreEVM @@ -206,13 +209,13 @@ from manticore.ethereum import ManticoreEVM m = ManticoreEVM() ``` -Une transaction brute est exécutée en utilisant [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account): +Un compte externe (non-contrat) est créé à l'aide de [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) : ```python user_account = m.create_account(balance=1000) ``` -Une transaction brute est exécutée en utilisant [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract): +Un contrat Solidity peut être déployé à l'aide de [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract) : ```solidity source_code = ''' @@ -225,24 +228,24 @@ contract Simple { } } ''' -# Init le contrat +# Initialiser le contrat contract_account = m.solidity_create_contract(source_code, owner=user_account) ``` #### Résumé {#summary} -- Vous pouvez créer des comptes utilisateur et de contrat avec [m.create_account](https://manticore.readthedocs.io/en/latest/api.html#manticore.ethereum.ManticoreEVM.create_account) et [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract). +- Vous pouvez créer des comptes d'utilisateur et des comptes de contrat avec [m.create_account](https://manticore.readthedocs.io/en/latest/evm.html?highlight=create_account#manticore.ethereum.ManticoreEVM.create_account) et [m.solidity_create_contract](https://manticore.readthedocs.io/en/latest/evm.html?highlight=solidity_create#manticore.ethereum.ManticoreEVM.create_contract). -### Exécution des transactions {#executing-transactions} +### Exécuter des transactions {#executing-transactions} -Manticore supporte deux types de transactions : +Manticore prend en charge deux types de transactions : - Transaction brute : toutes les fonctions sont explorées - Transaction nommée : une seule fonction est explorée #### Transaction brute {#raw-transaction} -Une transaction brute est exécutée en utilisant [m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction) : +Une transaction brute est exécutée à l'aide de [m.transaction](https://manticore.readthedocs.io/en/latest/evm.html?highlight=transaction#manticore.ethereum.ManticoreEVM.transaction) : ```python m.transaction(caller=user_account, @@ -251,10 +254,10 @@ m.transaction(caller=user_account, value=value) ``` -L'appelant, l'adresse, les données ou la valeur de la transaction peut être soit concrète ou symbolique : +L'appelant, l'adresse, les données ou la valeur de la transaction peuvent être soit concrets, soit symboliques : - [m.make_symbolic_value](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_value#manticore.ethereum.ManticoreEVM.make_symbolic_value) crée une valeur symbolique. -- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer) crée une valeur symbolique. +- [m.make_symbolic_buffer(size)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=make_symbolic_buffer#manticore.ethereum.ManticoreEVM.make_symbolic_buffer) crée un tableau d'octets symbolique. Par exemple : @@ -267,40 +270,41 @@ m.transaction(caller=user_account, value=symbolic_value) ``` -Si les données sont symboliques, Manticore explorera toutes les fonctions du contrat pendant l'exécution de la transaction. Il sera utile de voir l'explication de la fonction de repli dans l'article [Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/) pour comprendre comment fonctionne la sélection de fonctions. +Si les données sont symboliques, Manticore explorera toutes les fonctions du contrat pendant l'exécution de la transaction. Il sera utile de consulter l'explication de la fonction de secours dans l'article [Hands on the Ethernaut CTF](https://blog.trailofbits.com/2017/11/06/hands-on-the-ethernaut-ctf/) pour comprendre comment fonctionne la sélection de fonction. #### Transaction nommée {#named-transaction} -Les fonctions peuvent être exécutées via leur nom. Pour exécuter `f(uint var)` avec une valeur symbolique, à partir de user_account et avec 0 ether, utilisez : +Les fonctions peuvent être exécutées via leur nom. +Pour exécuter `f(uint var)` avec une valeur symbolique, à partir de user_account et avec 0 ether, utilisez : ```python symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var, caller=user_account, value=0) ``` -Si la `value` de la transaction n'est pas spécifiée, elle est de 0 par défaut. +Si la `valeur` de la transaction n'est pas spécifiée, elle est de 0 par défaut. #### Résumé {#summary-1} - Les arguments d'une transaction peuvent être concrets ou symboliques - Une transaction brute explorera toutes les fonctions -- Les fonctions peuvent être exécutées via leurs noms +- Les fonctions peuvent être appelées par leur nom ### Espace de travail {#workspace} `m.workspace` est le répertoire utilisé comme répertoire de sortie pour tous les fichiers générés : ```python -print("Les resultats sont dans {}".format(m.workspace)) +print("Les résultats se trouvent dans {}".format(m.workspace)) ``` ### Terminer l'exploration {#terminate-the-exploration} -Pour arrêter l'exploration utilisez [m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize). Aucune autre transaction ne devrait être envoyée une fois que cette méthode est appelée et Manticore génère des scénarios de test pour chaque chemin exploré. +Pour arrêter l'exploration, utilisez [m.finalize()](https://manticore.readthedocs.io/en/latest/evm.html?highlight=finalize#manticore.ethereum.ManticoreEVM.finalize). Aucune autre transaction ne devrait être envoyée une fois cette méthode appelée. Manticore génère alors des cas de test pour chaque chemin exploré. -### Résumé: Exécution sous Manticore {#summary-running-under-manticore} +### Résumé : Exécution sous Manticore {#summary-running-under-manticore} -En réunissant toutes les étapes précédentes, nous obtenons: +En réunissant toutes les étapes précédentes, nous obtenons : ```python from manticore.ethereum import ManticoreEVM @@ -316,15 +320,15 @@ contract_account = m.solidity_create_contract(source_code, owner=user_account) symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var) -print("Results are in {}".format(m.workspace)) -m.finalize() # stop the exploration +print("Les résultats se trouvent dans {}".format(m.workspace)) +m.finalize() # arrête l'exploration ``` -Vous trouverez tout le code ci-dessus dans le fichier [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) +Vous pouvez retrouver tout le code ci-dessus dans [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) -## Obtenir des chemins de lancement {#getting-throwing-paths} +## Obtenir les chemins qui lèvent une exception {#getting-throwing-paths} -Nous allons maintenant générer des entrées spécifiques pour les chemins soulevant une exception dans `f()`. La cible est le contrat intelligent suivant [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): +Nous allons maintenant générer des entrées spécifiques pour les chemins levant une exception dans `f()`. La cible est toujours le contrat intelligent suivant [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol) : ```solidity pragma solidity >=0.4.24 <0.6.0; @@ -337,17 +341,17 @@ contract Simple { } ``` -### Utiliser les informations sur l'état d'objet {#using-state-information} +### Utiliser les informations d'état {#using-state-information} -Chaque chemin exécuté a son état de la blockchain. Un état est soit prêt, soit il est tué, ce qui signifie qu'il atteint une instruction THROW ou REVERT : +Chaque chemin exécuté a son propre état de la blockchain. Un état est soit prêt, soit il est tué, ce qui signifie qu'il atteint une instruction THROW ou REVERT : -- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing) : la liste des états qui sont prêts (ils n'ont pas exécuté de REVERT/INVALID) -- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings) : liste des états qui sont morts +- [m.ready_states](https://manticore.readthedocs.io/en/latest/states.html#accessing) : la liste des états qui sont prêts (ils n'ont pas exécuté d'instruction REVERT/INVALID) +- [m.killed_states](https://manticore.readthedocs.io/en/latest/states.html#accessings) : la liste des états qui sont tués - [m.all_states](https://manticore.readthedocs.io/en/latest/states.html#accessings) : tous les états ```python for state in m.all_states: - # do something with state + # faire quelque chose avec l'état ``` Vous pouvez accéder aux informations d'état. Par exemple : @@ -363,7 +367,7 @@ data = state.platform.transactions[0].return_data data = ABI.deserialize("uint", data) ``` -### Comment générer des cas de test {#how-to-generate-testcase} +### Comment générer un cas de test {#how-to-generate-testcase} Utilisez [m.generate_testcase(state, name)](https://manticore.readthedocs.io/en/latest/evm.html?highlight=generate_testcase#manticore.ethereum.ManticoreEVM.generate_testcase) pour générer un cas de test : @@ -373,13 +377,13 @@ m.generate_testcase(state, 'BugFound') ### Résumé {#summary-2} -- Vous pouvez itérer sur l'état avec m.all_states +- Vous pouvez itérer sur l'état avec `m.all_states` - `state.platform.get_balance(account.address)` retourne le solde du compte - `state.platform.transactions` retourne la liste des transactions -- `transaction.return_data` constitue les données retournées +- `transaction.return_data` correspond aux données retournées - `m.generate_testcase(state, name)` génère des entrées pour l'état -### Résumé : Obtenir un chemin de lancement {#summary-getting-throwing-path} +### Résumé : Obtenir le chemin qui lève une exception {#summary-getting-throwing-path} ```python from manticore.ethereum import ManticoreEVM @@ -395,21 +399,23 @@ contract_account = m.solidity_create_contract(source_code, owner=user_account) symbolic_var = m.make_symbolic_value() contract_account.f(symbolic_var) -## Verifie si l’execution se termine avec REVERT ou INVALID +## Vérifier si une exécution se termine par REVERT ou INVALID + for state in m.terminated_states: last_tx = state.platform.transactions[-1] if last_tx.result in ['REVERT', 'INVALID']: - print('Throw found {}'.format(m.workspace)) + print('Exception trouvée {}'.format(m.workspace)) m.generate_testcase(state, 'ThrowFound') ``` -Vous trouverez tout le code ci-dessus dans le fichier [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) +Vous pouvez retrouver tout le code ci-dessus dans [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) -_Notez que nous aurions pu générer un script beaucoup plus simple, comme tous les états retournés par terminated_state ont REVERT ou INVALID dans leur résultat : cet exemple était uniquement destiné à montrer comment manipuler l'API._ +_Remarque : nous aurions pu générer un script beaucoup plus simple, car tous les états renvoyés par `terminated_state` ont REVERT ou INVALID dans leur résultat. Cet exemple visait uniquement à montrer comment manipuler l'API._ ## Ajouter des contraintes {#adding-constraints} -Nous verrons comment limiter l'exploration. Nous ferons l'hypothèse que la documentation de `f()` indique que la fonction n'est jamais appelée avec `a == 65`, donc tout bogue avec `a == 65` n'est pas un vrai bogue. La cible est le contrat intelligent suivant [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol): +Nous allons voir comment contraindre l'exploration. Nous ferons l'hypothèse que la +documentation de `f()` indique que la fonction n'est jamais appelée avec `a == 65`, donc tout bug avec `a == 65` n'est pas un vrai bug. La cible est toujours le contrat intelligent suivant [`example.sol`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example.sol) : ```solidity pragma solidity >=0.4.24 <0.6.0; @@ -424,14 +430,14 @@ contract Simple { ### Opérateurs {#operators} -Le module [Opérateurs](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py) facilite la manipulation des contraintes, entre autres il fournit : +Le module [Opérateurs](https://github.com/trailofbits/manticore/blob/master/manticore/core/smtlib/operators.py) facilite la manipulation des contraintes. Il fournit entre autres : - Operators.AND, - Operators.OR, -- Operators.UGT (unsigned greater than), -- Operators.UGE (unsigned greater than or equal to), -- Operators.ULT (unsigned lower than), -- Operators.ULE (unsigned lower than or equal to). +- Operators.UGT (supérieur à, non signé), +- Operators.UGE (supérieur ou égal à, non signé), +- Operators.ULT (inférieur à, non signé), +- Operators.ULE (inférieur ou égal à, non signé). Pour importer le module, utilisez ce qui suit : @@ -439,7 +445,7 @@ Pour importer le module, utilisez ce qui suit : from manticore.core.smtlib import Operators ``` -`Operators.CONCAT` est utilisé pour concaténer une table à une valeur. Par exemple, la valeur return_data d'une transaction doit être remplacée par une valeur à vérifier avec une autre valeur : +`Operators.CONCAT` est utilisé pour concaténer un tableau à une valeur. Par exemple, les `return_data` d'une transaction doivent être transformées en une valeur pour être comparées à une autre valeur : ```python last_return = Operators.CONCAT(256, *last_return) @@ -447,11 +453,12 @@ last_return = Operators.CONCAT(256, *last_return) ### Contraintes {#state-constraint} -Vous pouvez utiliser des contraintes globalement ou pour un état spécifique. +Vous pouvez utiliser des contraintes de manière globale ou pour un état spécifique. #### Contrainte globale {#state-constraint} -Utilisez `m.constrain(constraint)` pour ajouter une contrainte globale. Par exemple, vous pouvez appeler un contrat à partir d'une adresse symbolique, et restreindre cette adresse à des valeurs spécifiques: +Utilisez `m.constrain(constraint)` pour ajouter une contrainte globale. +Par exemple, vous pouvez appeler un contrat depuis une adresse symbolique et restreindre cette adresse à des valeurs spécifiques : ```python symbolic_address = m.make_symbolic_value() @@ -464,21 +471,23 @@ m.transaction(caller=user_account, #### Contrainte d'état {#state-constraint} -Utiliser l'état [state.constrain(contrainte)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain) pour ajouter une contrainte à un état spécifique Il peut être utilisé pour contraindre l'état après son exploration pour vérifier une propriété dessus. +Utilisez [state.constrain(constraint)](https://manticore.readthedocs.io/en/latest/states.html?highlight=StateBase#manticore.core.state.StateBase.constrain) pour ajouter une contrainte à un état spécifique. +Cela peut être utilisé pour contraindre l'état après son exploration afin de vérifier une propriété sur celui-ci. -### Vérifier les contraintes {#checking-constraint} +### Vérification de contrainte {#checking-constraint} -Utilisez `solver.check(state.contraints)` pour savoir si une contrainte est toujours faisable. Par exemple, ce qui suit contraindra symbolic_value à être différent de 65 et vérifiera si l'état est toujours réalisable : +Utilisez `solver.check(state.constraints)` pour savoir si une contrainte est toujours réalisable. +Par exemple, ce qui suit contraindra `symbolic_value` à être différent de 65 et vérifiera si l'état est toujours réalisable : ```python state.constrain(symbolic_var != 65) if solver.check(state.constraints): - # l’etat est possible + # l'état est réalisable ``` -### Résumé: Ajouter des contraintes {#summary-adding-constraints} +### Résumé : Ajouter des contraintes {#summary-adding-constraints} -En ajoutant des contraintes au code précédent, nous obtenons : +En ajoutant une contrainte au code précédent, nous obtenons : ```python from manticore.ethereum import ManticoreEVM @@ -499,18 +508,19 @@ contract_account.f(symbolic_var) no_bug_found = True -## Verifie si l’execution se termine par REVERT ou INVALID +## Vérifier si une exécution se termine par REVERT ou INVALID + for state in m.terminated_states: last_tx = state.platform.transactions[-1] if last_tx.result in ['REVERT', 'INVALID']: - # on ne considere pas le chemin quand a==65 + # nous ne considérons pas le chemin où a == 65 condition = symbolic_var != 65 if m.generate_testcase(state, name="BugFound", only_if=condition): - print(f'Bug trouve, les resultats sont dans {m.workspace}') + print(f'Bug trouvé, les résultats sont dans {m.workspace}') no_bug_found = False if no_bug_found: - print(f'Pas de bug trouve') + print(f'Aucun bug trouvé') ``` -Vous trouverez tout le code ci-dessus dans le fichier [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) +Vous pouvez retrouver tout le code ci-dessus dans [`example_run.py`](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/manticore/examples/example_run.py) diff --git a/public/content/translations/fr/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md b/public/content/translations/fr/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md index 5c6198a7ca4..f67d5830300 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/index.md @@ -1,49 +1,50 @@ --- -title: Comment utiliser Slither pour trouver des bugs de contrat intelligent -description: Comment utiliser Slither pour trouver automatiquement des bugs dans les contrats intelligents +title: Comment utiliser Slither pour trouver des bogues dans les contrats intelligents +description: Comment utiliser Slither pour trouver automatiquement des bogues dans les contrats intelligents author: Trailofbits lang: fr tags: - - "solidity" - - "contrats intelligents" - - "sécurité" - - "test" - - "analyse statique" + [ + "solidité", + "contrats intelligents", + "sécurité", + "test" + ] skill: advanced published: 2020-06-09 -source: Créer des contrats sécurisés +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither --- ## Comment utiliser Slither {#how-to-use-slither} -Le but de ce tutoriel est de démontrer comment utiliser Slither pour trouver automatiquement des bugs dans les contrats intelligents. +Le but de ce tutoriel est de montrer comment utiliser Slither pour trouver automatiquement des bogues dans les contrats intelligents. - [Installation](#installation) -- [Utilisation des lignes de commande](#command-line) -- [Introduction à l'analyse statique](#static-analysis): Brève introduction à l'analyse statique -- [API](#api-basics) : Description de l'API Python +- [Utilisation de la ligne de commande](#command-line) +- [Introduction à l'analyse statique](#static-analysis) : brève introduction à l'analyse statique +- [API](#api-basics) : description de l'API Python ## Installation {#installation} -Slither nécessite Python >= 3.6. Il peut être installé via pip ou avec docker. +Slither requiert Python >= 3.6. Il peut être installé via pip ou en utilisant docker. -Slither via pip : +Slither via pip : ```bash pip3 install --user slither-analyzer ``` -Slither via docker : +Slither via docker : ```bash docker pull trailofbits/eth-security-toolbox docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox ``` -_La dernière commande exécute eth-security-toolbox dans un docker qui a accès à votre répertoire courant. Vous pouvez changer les fichiers depuis votre hôte et exécuter les outils sur les fichiers depuis le docker_ +_La dernière commande exécute eth-security-toolbox dans un docker qui a accès à votre répertoire actuel. Vous pouvez modifier les fichiers depuis votre hôte, et exécuter les outils sur les fichiers depuis le docker_ -Dans docker, lancez : +À l'intérieur de docker, exécutez : ```bash solc-select 0.5.11 @@ -60,37 +61,37 @@ python3 script.py ### Ligne de commande {#command-line} -**Ligne de commande contre scripts définis par l'utilisateur.** Slither est livré avec un ensemble de détecteurs prédéfinis qui trouvent beaucoup de bogues communs. Faire appel à Slither à partir de la ligne de commande exécutera tous les détecteurs, aucune connaissance détaillée de l'analyse statique requise : +**Ligne de commande ou scripts définis par l'utilisateur.** Slither est livré avec un ensemble de détecteurs prédéfinis qui trouvent de nombreux bogues courants. Appeler Slither depuis la ligne de commande exécutera tous les détecteurs, aucune connaissance détaillée de l'analyse statique n'est requise : ```bash slither project_paths ``` -En plus des détecteurs, Slither dispose de capacités de révision de code grâce à ses [printers](https://github.com/crytic/slither#printers) et [outils](https://github.com/crytic/slither#tools). +En plus des détecteurs, Slither dispose de fonctionnalités de révision de code via ses [printers](https://github.com/crytic/slither#printers) et ses [tools](https://github.com/crytic/slither#tools). -Utilisez [crytic.io](https://github.com/crytic) pour avoir accès à des détecteurs privés et à l'intégration GitHub. +Utilisez [crytic.io](https://github.com/crytic) pour accéder à des détecteurs privés et à l'intégration GitHub. ## Analyse statique {#static-analysis} -Les capacités et la conception du cadre d'analyse statique Slither ont été décrites dans les articles de blog ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) et un [document académique](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf). +Les capacités et la conception du framework d'analyse statique Slither ont été décrites dans des articles de blog ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) et un [article académique](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf). -L'analyse statique existe dans différentes saveurs. Vous réalisez très probablement que les compilateurs comme [clang](https://clang-analyzer.llvm.org/) et [gcc](https://lwn.net/Articles/806099/) dépendent de ces techniques de recherche, mais il soutient aussi ([Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/) et les outils basés sur des méthodes formelles telles que [Frama-C](https://frama-c.com/) et [Polyspace](https://www.mathworks.com/products/polyspace.html). +L'analyse statique se présente sous différentes formes. Vous savez probablement que les compilateurs comme [clang](https://clang-analyzer.llvm.org/) et [gcc](https://lwn.net/Articles/806099/) s'appuient sur ces techniques de recherche, mais elles sous-tendent également ([Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/) et des outils basés sur des méthodes formelles comme [Frama-C](https://frama-c.com/) et [Polyspace](https://www.mathworks.com/products/polyspace.html)). -Nous ne passerons pas en revue de façon exhaustive les techniques d'analyse statique et le chercheur ici. Au lieu de cela, nous nous concentrerons sur ce qui est nécessaire pour comprendre le fonctionnement de Slither afin que vous puissiez l'utiliser plus efficacement pour trouver des bogues et comprendre du code. +Nous n'allons pas passer en revue de manière exhaustive les techniques d'analyse statique et les chercheurs ici. Au lieu de cela, nous nous concentrerons sur ce qui est nécessaire pour comprendre le fonctionnement de Slither afin que vous puissiez l'utiliser plus efficacement pour trouver des bogues et comprendre le code. - [Représentation du code](#code-representation) -- [Analyse de code](#analysis) -- [Représentation Intermédiaire](#intermediate-representation) +- [Analyse du code](#analysis) +- [Représentation intermédiaire](#intermediate-representation) ### Représentation du code {#code-representation} -Contrairement à une analyse dynamique, qui explique les raisons d'un seul chemin d'exécution, l'analyse statique explique tous les chemins en même temps. Pour ce faire, il repose sur une représentation du code différente. Les deux plus courants sont l'arborescence de syntaxe abstraite (AST) et le graphique de flux de contrôle (CFG). +Contrairement à une analyse dynamique, qui raisonne sur un seul chemin d'exécution, l'analyse statique raisonne sur tous les chemins à la fois. Pour ce faire, elle s'appuie sur une représentation différente du code. Les deux plus courantes sont l'arbre de syntaxe abstraite (AST) et le graphe de flot de contrôle (CFG). -### Arbres syntaxiques abstraits (AST) {#abstract-syntax-trees-ast} +### Arbres de syntaxe abstraits (AST) {#abstract-syntax-trees-ast} -AST est utilisé chaque fois que le compilateur analyse le code. C'est probablement la structure la plus basique sur laquelle une analyse statique peut être effectuée. +Les AST sont utilisés chaque fois que le compilateur analyse le code. C'est probablement la structure la plus élémentaire sur laquelle une analyse statique peut être effectuée. -En un mot, un AST est un arbre structuré où, habituellement, chaque feuille contient une variable ou une constante, et les nœuds internes sont des opérandes ou des opérations de contrôle de flux. Considérez les codes suivants : +En bref, un AST est un arbre structuré où, généralement, chaque feuille contient une variable ou une constante et les nœuds internes sont des opérandes ou des opérations de flux de contrôle. Considérons le code suivant : ```solidity function safeAdd(uint a, uint b) pure internal returns(uint){ @@ -101,15 +102,15 @@ function safeAdd(uint a, uint b) pure internal returns(uint){ } ``` -L'AST correspondant est affiché dans : +L'AST correspondant est présenté ci-dessous : ![AST](./ast.png) Slither utilise l'AST exporté par solc. -Bien que simple à construire, l'AST est une structure imbriquée. Parfois, ce n'est pas le plus simple à analyser. Par exemple, pour identifier les opérations utilisées par l'expression `a + b <= a`, vous devez d'abord analyser `<=` puis `+`. Une approche commune est d'utiliser le design pattern « visiteur » pour naviguer dans l'arbre récursivement. Slither contient un visiteur générique dans [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py). +Bien que simple à construire, l'AST est une structure imbriquée. Parfois, ce n'est pas la plus simple à analyser. Par exemple, pour identifier les opérations utilisées par l'expression `a + b <= a`, vous devez d'abord analyser `<=` puis `+`. Une approche courante consiste à utiliser le patron de conception visiteur, qui parcourt l'arbre de manière récursive. Slither contient un visiteur générique dans [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py). -Le code suivant utilise `ExpressionVisitor` pour détecter si l'expression contient un ajout : +Le code suivant utilise `ExpressionVisitor` pour détecter si l'expression contient une addition : ```python from slither.visitors.expression.expression import ExpressionVisitor @@ -124,58 +125,58 @@ class HasAddition(ExpressionVisitor): if expression.type == BinaryOperationType.ADDITION: self._result = True -visitor = HasAddition(expression) # expression correspond a l'expression qui sera testé -print(f'L’expression {expression} contient une addition: {visitor.result()}') +visitor = HasAddition(expression) # expression est l'expression à tester +print(f'L\'expression {expression} contient une addition : {visitor.result()}') ``` -### Graphe de contrôle du flux (CFG) {#control-flow-graph-cfg} +### Graphe de flot de contrôle (CFG) {#control-flow-graph-cfg} -La deuxième représentation de code la plus courante est le graphique de flux de contrôle (CFG). Comme son nom l'indique, il s'agit d'une représentation graphique qui expose tous les chemins d'exécution. Chaque nœud contient une ou plusieurs instructions. Les bords dans le graphique représentent les opérations de contrôle du flux (if/then/else, loop, etc.). Le CFG de notre exemple précédent est : +La deuxième représentation de code la plus courante est le graphe de flot de contrôle (CFG). Comme son nom l'indique, il s'agit d'une représentation basée sur un graphe qui expose tous les chemins d'exécution. Chaque nœud contient une ou plusieurs instructions. Les arêtes du graphe représentent les opérations de flux de contrôle (if/then/else, boucle, etc.). Le CFG de notre exemple précédent est : ![CFG](./cfg.png) Le CFG est la représentation sur laquelle la plupart des analyses sont construites. -De nombreuses autres représentations de code existent. Chaque représentation a des avantages et des inconvénients en fonction de l'analyse que vous voulez effectuer. +Il existe de nombreuses autres représentations du code. Chaque représentation a ses avantages et ses inconvénients en fonction de l'analyse que vous souhaitez effectuer. ### Analyse {#analysis} -Les analyses les plus simples que vous pouvez effectuer avec Slither sont des analyses syntaxiques. +Les types d'analyses les plus simples que vous pouvez effectuer avec Slither sont les analyses syntaxiques. ### Analyse syntaxique {#syntax-analysis} -Slither peut naviguer à travers les différents composants du code et de leur représentation pour trouver des incohérences et des défauts en utilisant une approche similaire à la recherche de modèles. +Slither peut naviguer à travers les différents composants du code et leur représentation pour trouver des incohérences et des défauts en utilisant une approche de type reconnaissance de formes. -Par exemple, les détecteurs suivants recherchent les problèmes liés à la syntaxe : +Par exemple, les détecteurs suivants recherchent des problèmes liés à la syntaxe : -- [State variavle shadowing>](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing) : itère sur toutes les variables d'état et vérifie si une variable est masquée à partir d'un contrat hérité ([état. y#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) +- [Masquage de variable d'état](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing) : itère sur toutes les variables d'état et vérifie si l'une d'entre elles masque une variable d'un contrat hérité ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) -- [Interface ERC20 incorrecte](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface) : recherche des signatures de fonction ERC20 incorrectes ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) +- [Interface ERC20 incorrecte](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface) : recherche les signatures de fonction ERC20 incorrectes ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) ### Analyse sémantique {#semantic-analysis} -Contrairement à l'analyse syntaxique, une analyse sémantique ira plus loin et analysera le « sens » du code. Cette famille comprend quelques grands types d'analyse. Ils conduisent à des résultats plus puissants et utiles, mais sont également plus complexes à rédiger. +Contrairement à l'analyse syntaxique, une analyse sémantique va plus loin et analyse la « signification » du code. Cette famille comprend quelques grands types d'analyses. Elles mènent à des résultats plus puissants et utiles, mais sont aussi plus complexes à écrire. -Les analyses sémantiques sont utilisées pour les détections de vulnérabilité les plus avancées. +Les analyses sémantiques sont utilisées pour les détections de vulnérabilités les plus avancées. -#### Analyse des dépendances des données {#fixed-point-computation} +#### Analyse de dépendance des données {#fixed-point-computation} -Une variable `variable_a` est dite dépendante des données de `variable_b` s'il y a un chemin pour lequel la valeur de `variable_a` est influencée par `variable_b`. +Une variable `variable_a` est dite dépendante des données de `variable_b` s'il existe un chemin pour lequel la valeur de `variable_a` est influencée par `variable_b`. -Dans le code suivant, `variable_a` est dépendant de `variable_b`: +Dans le code suivant, `variable_a` dépend de `variable_b` : ```solidity // ... variable_a = variable_b + 1; ``` -Slither est livré avec des capacités intégrées grâce à sa représentation intermédiaire (discutée dans une section ultérieure). +Slither dispose de fonctionnalités intégrées de [dépendance des données](https://github.com/crytic/slither/wiki/data-dependency), grâce à sa représentation intermédiaire (abordée dans une section ultérieure). -Un exemple d'utilisation de la dépendance des données peut être trouvé dans le [dangereux détecteur strict d'égalité](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Ici, Slither recherchera une comparaison stricte de l'égalité avec une valeur dangereuse ([incorrect_strict_equality. y#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)), et informera l'utilisateur qu'il doit utiliser `>=` ou `<=` au lieu de `==`, pour empêcher un attaquant de piéger le contrat. Entre autres, le détecteur considérera comme dangereux la valeur de retour d'un appel à `balanceOf(address)` ([incorrect_strict_equality. y#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)), et utilisera le moteur de dépendance de données pour suivre son utilisation. +Un exemple d'utilisation de la dépendance des données se trouve dans le [détecteur d'égalité stricte dangereuse](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Ici, Slither recherchera une comparaison d'égalité stricte à une valeur dangereuse ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)), et informera l'utilisateur qu'il doit utiliser `>=` ou `<=` plutôt que `==`, pour empêcher un attaquant de piéger le contrat. Entre autres, le détecteur considérera comme dangereuse la valeur de retour d'un appel à `balanceOf(address)` ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)), et utilisera le moteur de dépendance des données pour suivre son utilisation. -#### Calcul à decimales fixes {#fixed-point-computation} +#### Calcul de point fixe {#fixed-point-computation} -Si votre analyse navigue à travers le CFG et suit les bords, vous êtes susceptible de voir des nœuds déjà visités. Par exemple, si une boucle est présentée tel qu'illustré ci-dessous : +Si votre analyse parcourt le CFG et suit les arêtes, vous êtes susceptible de voir des nœuds déjà visités. Par exemple, si une boucle est présentée comme ci-dessous : ```solidity for(uint i; i < range; ++){ @@ -183,56 +184,56 @@ for(uint i; i < range; ++){ } ``` -Votre analyse devra savoir quand s'arrêter. Il y a deux stratégies principales ici : (1) itérer sur chaque noeud un nombre limité de fois, (2) calculer un _fixpoint_. Un point fixe signifie que l'analyse de ce noeud ne fournit aucune information significative. +Votre analyse devra savoir quand s'arrêter. Il y a deux stratégies principales ici : (1) itérer sur chaque nœud un nombre fini de fois, (2) calculer un soi-disant _point fixe_. Un point fixe signifie essentiellement que l'analyse de ce nœud ne fournit plus aucune information significative. -Un exemple de point fixe utilisé peut être trouvé dans les détecteurs de rétractation : Slither explore les nœuds et il est possible de chercher des appels externes, écrire et lire sur le stockage. Une fois qu'il a atteint un point fixe ([réentrance. y#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), il arrête l'exploration, et analyse les résultats pour voir si une réentrance est présente, à travers différents modèles de réentrance ([reentrancy_benign. y](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)). +Un exemple d'utilisation de point fixe se trouve dans les détecteurs de réentrance : Slither explore les nœuds et recherche les appels externes, l'écriture et la lecture dans le stockage. Une fois qu'il a atteint un point fixe ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), il arrête l'exploration et analyse les résultats pour voir si une réentrance est présente, à travers différents modèles de réentrance ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)). -Écrire des analyses à l'aide de calculs fixes efficaces nécessite une bonne compréhension de la manière dont l'analyse propage ses informations. +L'écriture d'analyses utilisant un calcul de point fixe efficace requiert une bonne compréhension de la manière dont l'analyse propage ses informations. -### Représentation Intermédiaire {#intermediate-representation} +### Représentation intermédiaire {#intermediate-representation} -Une représentation intermédiaire (IR) est un langage destiné à être plus apte à une analyse statique que le langage original. Slither traduit Solidity à son propre IR : [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). +Une représentation intermédiaire (RI) est un langage destiné à être plus adapté à l'analyse statique que le langage original. Slither traduit Solidity dans sa propre RI : [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). -Comprendre SlithIR n'est pas nécessaire si vous voulez seulement écrire des vérifications de base. Cependant, il sera pratique si vous prévoyez d'écrire des analyses sémantiques avancées. Les imprimantes [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) et [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) vous aideront à comprendre comment le code est traduit. +Comprendre SlithIR n'est pas nécessaire si vous voulez seulement écrire des vérifications de base. Cependant, ce sera utile si vous prévoyez d'écrire des analyses sémantiques avancées. Les printers [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) et [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) vous aideront à comprendre comment le code est traduit. -## Les bases de l’API {#api-basics} +## Principes de base de l'API {#api-basics} -Slither a une API qui vous permet d'explorer les attributs de base du contrat et de ses fonctions. +Slither dispose d'une API qui vous permet d'explorer les attributs de base du contrat et de ses fonctions. -Pour charger le code base : +Pour charger une base de code : ```python from slither import Slither -slither = Slither('/chemin/vers/projet') +slither = Slither('/chemin/vers/le/projet') ``` -### Exploration des contrats et fonctions {#exploring-contracts-and-functions} +### Explorer les contrats et les fonctions {#exploring-contracts-and-functions} -Un objet `Slither` a : +Un objet `Slither` possède : -- `contrats (list(Contrat)` : liste des contrats -- `contracts_derived (list(Contrat)` : liste des contrats qui ne sont pas hérités par un autre contrat (sous-ensemble de contrats) -- `get_contract_from_name (str)` : Renvoie un contrat à partir de son nom +- `contracts (list(Contract)` : liste de contrats +- `contracts_derived (list(Contract)` : liste de contrats qui ne sont pas hérités par un autre contrat (sous-ensemble de contrats) +- `get_contract_from_name (str)` : renvoie un contrat à partir de son nom -Un objet `Contract` a : +Un objet `Contract` possède : -- `name(str)` : Nom du contrat -- `fonctions(list(fonction))` : Liste des fonctions -- `modifiers(list(Modifier))` : Liste des fonctions -- `all_functions_called(list(Function/Modifier))` : Liste de toutes les fonctions internes accessibles par le contrat -- `inheritance(list(Contract))` : Liste des contrats hérités -- `get_function_from_signature(str)` : Renvoie une fonction à partir de sa signature -- `get_modifier_from_signature(str)` : Renvoie un Modifier à partir de sa signature -- `get_state_variable_from_name(str)` : Renvoie une StateVariable à partir de son nom +- `name (str)` : nom du contrat +- `functions (list(Function))` : liste des fonctions +- `modifiers (list(Modifier))` : liste de modificateurs +- `all_functions_called (list(Function/Modifier))` : liste de toutes les fonctions internes accessibles par le contrat +- `inheritance (list(Contract))` : liste des contrats hérités +- `get_function_from_signature (str)` : renvoie une fonction à partir de sa signature +- `get_modifier_from_signature (str)` : renvoie un modificateur à partir de sa signature +- `get_state_variable_from_name (str)` : renvoie une StateVariable à partir de son nom -Un objet `Function` ou un `Modifier` a : +Un objet `Function` ou `Modifier` possède : -- `name(str)` : Nom de la fonction -- `contract (contract)` : le contrat où la fonction est déclarée -- `nodes(list(Node))` : Liste des noeuds composant la fonction/modifier de CFG -- `entry_point(Node)` : Point d’entrée du CFG -- `variables_read(list(Variable))` : Liste des variables de lecture -- `variables_written(list(Variable))` : Liste des variable d’écritures -- `state_variables_read(list(StateVariable))` : Liste des variables d’états de lectures (sous-ensemble de lecture des variables) -- `state_variables_read(list(StateVariable))` : Liste des variables d’états de lectures (sous-ensemble de lecture des variables) +- `name (str)` : nom de la fonction +- `contract (contract)` : le contrat où la fonction est déclarée +- `nodes (list(Node))` : liste des nœuds composant le CFG de la fonction/du modificateur +- `entry_point (Node)` : point d'entrée du CFG +- `variables_read (list(Variable))` : liste des variables lues +- `variables_written (list(Variable))` : liste des variables écrites +- `state_variables_read (list(StateVariable))` : liste des variables d'état lues (sous-ensemble de `variables_read`) +- `state_variables_written (list(StateVariable))` : liste des variables d'état écrites (sous-ensemble de `variables_written`) diff --git a/public/content/translations/fr/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md b/public/content/translations/fr/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md index 95aa75cad9a..59a47771d42 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-use-tellor-as-your-oracle/index.md @@ -1,55 +1,52 @@ --- -title: Comment configurer Tellor comme Oracle -description: Un guide pour débuter l'intégration de l'oracle Tellor dans votre protocole +title: Comment configurer Tellor comme oracle +description: "Un guide pour débuter l'intégration de l'oracle Tellor dans votre protocole" author: "Tellor" lang: fr -tags: - - "solidity" - - "contrats intelligents" - - "oracles" +tags: [ "solidité", "contrats intelligents", "oracles" ] skill: beginner published: 2021-06-29 source: Tellor Docs sourceUrl: https://docs.tellor.io/tellor/ --- -Quiz Pop : Votre protocole est à peine terminé, mais il a besoin d'un oracle pour avoir accès aux données hors chaîne... Que faites-vous ? +Question surprise : votre protocole est presque terminé, mais il a besoin d'un oracle pour accéder aux données hors chaîne... Que faites-vous ? -## Prérequis (simple) {#soft-prerequisites} +## (Prérequis recommandés) {#soft-prerequisites} -Cet article a pour objectif de permettre l'accès à un flux oracle aussi simplement et efficacement que possible. Cela étant dit, nous supposons que votre niveau de compétence en codage est le suivant pour nous concentrer sur l'aspect oracle. +Cet article vise à rendre l'accès à un flux d'oracle aussi simple et direct que possible. Cela dit, nous partons du principe que vous avez les compétences de codage suivantes afin de nous concentrer sur l'aspect oracle. Hypothèses : - vous pouvez naviguer dans un terminal -- vous avez un npm installé -- vous savez comment utiliser le npm pour gérer les dépendances +- vous avez installé npm +- vous savez utiliser npm pour gérer les dépendances -Tellor est un oracle direct et open source prêt à l'emploi. Ce guide pour débutants a pour objectif de montrer la facilité d'utilisation de Tellor, et de doter votre projet d'un oracle entièrement décentralisé et résistant à la censure. +Tellor est un oracle actif et open source, prêt à être implémenté. Ce guide du débutant est là pour montrer la facilité avec laquelle on peut se lancer avec Tellor, fournissant à votre projet un oracle entièrement décentralisé et résistant à la censure. -## Aperçu {#overview} +## Vue d'ensemble {#overview} -Tellor est un système Oracle où les parties peuvent demander la valeur d'un point de données hors chaîne (par ex. BTC/USD). Les rapporteurs sont en concurrence pour ajouter cette valeur à une banque de données en chaîne accessible par tous les contrats intelligents Ethereum. Les entrées de cette banque de données sont sécurisées par un réseau de mises en jeu par des rapporteurs. Tellor utilise des mécanismes d'incitation crypto-économique, récompensant les déclarations honnêtes de données effectuées par des rapporteurs et punissant les mauvais acteurs grâce à la délivrance d'un jeton Tellor (les Tributes, ou TRB), et d'un mécanisme de conflit. +Tellor est un système d'oracle où les parties peuvent demander la valeur d'un point de données hors chaîne (p. ex., BTC/USD) et où des rapporteurs s'affrontent pour ajouter cette valeur à une banque de données en chaîne, accessible par tous les contrats intelligents Ethereum. Les entrées de cette banque de données sont sécurisées par un réseau de rapporteurs ayant mis des actifs en jeu. Tellor utilise des mécanismes d'incitation crypto-économiques, récompensant les soumissions de données honnêtes des rapporteurs et punissant les mauvais acteurs par l'émission du jeton de Tellor, Tributes (TRB), et un mécanisme de litige. -Dans ce tutoriel, nous allons passer en revue : +Dans ce tutoriel, nous aborderons : -- La mise en place de la boite à outils initiale dont vous aurez besoin pour démarrer. -- Un exemple simple. -- La liste des adresses testnet des réseaux sur lesquels vous pourrez tester Tellor. +- La configuration de la boîte à outils initiale dont vous aurez besoin pour être opérationnel. +- La présentation d'un exemple simple. +- La liste des adresses de réseaux de test sur lesquels vous pouvez actuellement tester Tellor. -## Utiliser Tellor {#usingtellor} +## UsingTellor {#usingtellor} -La première chose à faire est d'installer les outils de base nécessaires pour utiliser Tellor comme oracle. Utilisez [ce paquet](https://github.com/tellor-io/usingtellor) pour installer les contrats utilisateur de Tellor : +La première chose à faire est d'installer les outils de base nécessaires pour utiliser Tellor comme votre oracle. Utilisez [ce paquet](https://github.com/tellor-io/usingtellor) pour installer les contrats utilisateur de Tellor : `npm install usingtellor` -Une fois ces derniers installés, cela permettra à vos contrats d'hériter des fonctions du contrat 'UsingTellor'. +Une fois installé, cela permettra à vos contrats d'hériter des fonctions du contrat « UsingTellor ». -Génial ! Maintenant que vos outils sont prêts, intéressons-nous à un exercice simple où nous récupérons le prix de bitcoin : +Super ! Maintenant que vous avez préparé les outils, passons à un exercice simple où nous récupérons le prix du bitcoin : ### Exemple BTC/USD {#btcusd-example} -Hériter du contrat UsingTellor, en transmettant l'adresse Tellor en tant qu'argument de constructeur : +Héritez du contrat UsingTellor, en passant l'adresse Tellor comme argument du constructeur : Voici un exemple : @@ -59,13 +56,13 @@ import "usingtellor/contracts/UsingTellor.sol"; contract PriceContract is UsingTellor { uint256 public btcPrice; - //This Contract now has access to all functions in UsingTellor + //Ce contrat a maintenant accès à toutes les fonctions dans UsingTellor constructor(address payable _tellorAddress) UsingTellor(_tellorAddress) public {} function setBtcPrice() public { bytes memory _b = abi.encode("SpotPrice",abi.encode("btc","usd")); - bytes32 _queryID = keccak256(_b); + bytes32 _queryId = keccak256(_b); uint256 _timestamp; bytes _value; @@ -77,8 +74,8 @@ function setBtcPrice() public { } ``` -Pour une liste complète des adresses de contrat, veuillez cliquer [ici](https://docs.tellor.io/tellor/the-basics/contracts-reference). +Pour obtenir la liste complète des adresses de contrat, référez-vous à [ce document](https://docs.tellor.io/tellor/the-basics/contracts-reference). -Pour en faciliter l’utilisation, le dépôt de UsingTellor est livré avec une version du contrat [Tellor Playground](https://github.com/tellor-io/TellorPlayground) pour une intégration plus facile. Cliquez [ici](https://github.com/tellor-io/sampleUsingTellor#tellor-playground) pour une liste des fonctions utiles. +Pour en faciliter l'utilisation, le dépôt UsingTellor est fourni avec une version du contrat [Tellor Playground](https://github.com/tellor-io/TellorPlayground) pour une intégration plus facile. Consultez [cette page](https://github.com/tellor-io/sampleUsingTellor#tellor-playground) pour une liste des fonctions utiles. -Pour une implémentation plus robuste de l'oracle Tellor, consultez la liste complète des fonctions disponibles [ici.](https://github.com/tellor-io/usingtellor/blob/master/README.md) +Pour une implémentation plus robuste de l'oracle Tellor, consultez la liste complète des fonctions disponibles [ici](https://github.com/tellor-io/usingtellor/blob/master/README.md). diff --git a/public/content/translations/fr/developers/tutorials/how-to-view-nft-in-metamask/index.md b/public/content/translations/fr/developers/tutorials/how-to-view-nft-in-metamask/index.md index 3bd9e95879b..a149335d781 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-view-nft-in-metamask/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-view-nft-in-metamask/index.md @@ -1,35 +1,32 @@ --- -title: Comment voir votre NFT dans votre portefeuille (Partie 3/3 de la série des tutoriels NFT) -description: Ce tutoriel décrit comment visualiser un NFT existant sur MetaMask ! +title: "Comment voir votre NFT dans votre portefeuille (Partie 3/3 de la série de tutoriels NFT)" +description: "Ce tutoriel décrit comment visualiser un NFT existant sur MetaMask !" author: "Sumi Mudgil" -tags: - - "ERC-721" - - "Alchemy" - - "Solidity" +tags: [ "ERC-721", "Alchemy", "solidity" ] skill: beginner lang: fr published: 2021-04-22 --- -Ce tutoriel est la Partie 3/3 de la série de tutoriels NFT où l'on visualise nos NFT fraîchement créés. Vous pouvez toutefois utiliser le tutoriel général pour n'importe quel jeton ERC-721 en utilisant MetaMask, y compris sur le réseau principal ou un testnet. Si vous souhaitez apprendre à créer votre propre NFT sur Ethereum, veuillez consulter [la Partie 1 relative à la façon d'Écrire & Déployer un contrat intelligent NFT](/developers/tutorials/how-to-write-and-deploy-an-nft) ! +Ce tutoriel est la partie 3/3 de la série de tutoriels sur les NFT, où nous visualisons nos NFT fraîchement frappés. Vous pouvez toutefois utiliser le tutoriel général pour n'importe quel jeton ERC-721 en utilisant MetaMask, y compris sur le réseau principal ou n'importe quel réseau de test. Si vous souhaitez apprendre à frapper votre propre NFT sur Ethereum, consultez la [Partie 1 sur Comment écrire et déployer un contrat intelligent NFT](/developers/tutorials/how-to-write-and-deploy-an-nft) ! -Félicitations ! Vous avez réussi la partie la plus courte et la plus simple de notre série de tutoriels NFT — comment voir votre NFT fraîchement créé sur un portefeuille virtuel. Nous utiliserons MetaMask pour cet exemple, car nous l'avons utilisé dans les deux parties précédentes. +Félicitations ! Vous êtes arrivé à la partie la plus courte et la plus simple de notre série de tutoriels NFT — comment voir votre NFT fraîchement frappé sur un portefeuille virtuel. Nous utiliserons MetaMask pour cet exemple, car nous l'avons utilisé dans les deux parties précédentes. -En prérequis, vous devriez déjà avoir MetaMask installé sur votre mobile, et ce dernier devrait inclure le compte sur lequel vous avez miné votre NFT — vous pouvez télécharger l'application gratuitement sur [iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202) ou [Android](https://play.google.com/store/apps/details?id=io.metamask&hl=en_US&gl=US). +Comme prérequis, vous devez déjà avoir installé MetaMask sur votre mobile, et il doit inclure le compte sur lequel vous avez frappé votre NFT — vous pouvez obtenir l'application gratuitement sur [iOS](https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202) ou [Android](https://play.google.com/store/apps/details?id=io.metamask&hl=en_US&gl=US). ## Étape 1 : Configurez votre réseau sur Sepolia {#set-network-to-sepolia} -En haut de l'application, appuyez sur le bouton « Portefeuille », après quoi vous serez invité à sélectionner un réseau. Comme notre NFT a été miné sur le réseau Sepolia, vous devez sélectionner Sepolia comme votre réseau. +En haut de l'application, appuyez sur le bouton « Portefeuille », après quoi vous serez invité à sélectionner un réseau. Comme notre NFT a été frappé sur le réseau Sepolia, vous devrez sélectionner Sepolia comme votre réseau. -![Comment définir Sepolia comme votre réseau sur MetaMask Mobile](./goerliMetamask.gif) +![Comment configurer Sepolia comme votre réseau sur MetaMask Mobile](./goerliMetamask.gif) ## Étape 2 : Ajoutez votre article de collection à MetaMask {#add-nft-to-metamask} -Une fois que vous êtes sur le réseau Sepolia, sélectionner l'onglet « Articles de collection » sur la droite et ajoutez l'adresse du contrat intelligent NFT et l'identifiant du jeton ERC-721 de votre NFT — que vous devriez pouvoir trouver sur Etherscan en fonction du hachage de la transaction de votre NFT déployé dans la Partie II de notre tutoriel. +Une fois que vous êtes sur le réseau Sepolia, sélectionnez l'onglet « Articles de collection » sur la droite et ajoutez l'adresse du contrat intelligent NFT et l'ID du jeton ERC-721 de votre NFT — que vous devriez pouvoir trouver sur Etherscan en vous basant sur le hachage de la transaction de votre NFT déployé dans la Partie II de notre tutoriel. -![Comment trouver votre hachage de transaction et votre identifiant de jeton ERC-721](./findNFTEtherscan.png) +![Comment trouver le hachage de votre transaction et l'ID de votre jeton ERC-721](./findNFTEtherscan.png) -Vous devrez peut-être procéder à quelques actualisations pour voir votre NFT — mais il sera là ! +Vous devrez peut-être actualiser plusieurs fois pour voir votre NFT — mais il sera là ! ![Comment téléverser votre NFT sur MetaMask](./findNFTMetamask.gif) diff --git a/public/content/translations/fr/developers/tutorials/how-to-write-and-deploy-an-nft/index.md b/public/content/translations/fr/developers/tutorials/how-to-write-and-deploy-an-nft/index.md index 11f94fd11cb..1aebbed02e1 100644 --- a/public/content/translations/fr/developers/tutorials/how-to-write-and-deploy-an-nft/index.md +++ b/public/content/translations/fr/developers/tutorials/how-to-write-and-deploy-an-nft/index.md @@ -1,12 +1,14 @@ --- -title: Comment écrire & déployer un NFT (Partie 1/3 du tutoriel NFT) -description: Ce tutoriel est la première partie de la série sur les NFT et vous guidera pas-à-pas sur la façon d'écrire et de déployer un contrat intelligent de jeton non fongible (jeton ERC-721) avec Ethereum et IPFS (Inter Planetary File System). +title: "Comment écrire et déployer un NFT (Partie 1/3 de la série de tutoriels NFT)" +description: "Ce tutoriel est la première partie de la série sur les NFT et vous guidera pas-à-pas sur la façon d'écrire et de déployer un contrat intelligent de jeton non fongible (jeton ERC-721) avec Ethereum et IPFS (Inter Planetary File System)." author: "Sumi Mudgil" tags: - - "ERC-721" - - "Alchemy" - - "Solidity" - - "contrats intelligents" + [ + "ERC-721", + "Alchemy", + "solidity", + "contrats intelligents" + ] skill: beginner lang: fr published: 2021-04-22 @@ -14,72 +16,79 @@ published: 2021-04-22 Grâce aux NFT, la blockchain a été découverte par le grand public. C'est l'occasion rêvée de comprendre cet engouement en publiant votre propre contrat NFT (jeton ERC-721) sur la blockchain Ethereum ! -Alchemy est extrêmement fier d'alimenter les plus grands noms du monde des NFT, notamment Makersplace (qui a récemment établi un record de ventes d'œuvres d'art numériques chez Christie's pour 69 millions de dollars), Dapper Labs (créateurs de NBA Top Shot & Crypto Kitties), OpenSea (la plus grande place de marché NFT du monde), Zora, Super Rare, NFTfi, Foundation, Enjin, Origin Protocol, Immutable, et bien d'autres. +Alchemy est extrêmement fier d'alimenter les plus grands noms de l'espace NFT, y compris Makersplace (qui a récemment établi un record de vente d'œuvres d'art numériques chez Christie's pour 69 millions de dollars), Dapper Labs (créateurs de NBA Top Shot & Crypto Kitties), OpenSea (le plus grand marché NFT du monde), Zora, Super Rare, NFTfi, Foundation, Enjin, Origin Protocol, Immutable, et plus encore. -Dans ce tutoriel, nous allons créer et déployer un contrat intelligent ERC-721 sur le réseau de test Sepolia à l'aide de [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), [Pinata](https://pinata.cloud/) et [Alchemy](https://alchemy.com/signup/eth) (ne vous inquiétez pas si vous ne comprenez pas encore ce que cela signifie - nous vous l'expliquerons !). +Dans ce tutoriel, nous allons voir comment créer et déployer un contrat intelligent ERC-721 sur le réseau de test Sepolia en utilisant [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), [Pinata](https://pinata.cloud/) et [Alchemy](https://alchemy.com/signup/eth) (ne vous inquiétez pas si vous ne comprenez pas encore ce que tout cela signifie, nous allons tout vous expliquer !). Dans la deuxième partie de ce tutoriel, nous verrons comment utiliser notre contract intelligent pour créer un NFT, et dans la troisième partie, nous expliquerons comment visualiser votre NFT sur MetaMask. -Bien sûr, si vous avez des questions à n'importe quel moment, n'hésitez pas à contacter [le discord d'Alchemy](https://discord.gg/gWuC7zB) ou consultez [la documentation de l'API NFT d'Alchemy](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api) ! +Et bien sûr, si vous avez des questions, n'hésitez pas à les poser sur le [Discord d'Alchemy](https://discord.gg/gWuC7zB) ou à consulter la [documentation de l'API NFT d'Alchemy](https://docs.alchemy.com/alchemy/enhanced-apis/nft-api) ! ## Étape 1 : Se connecter au réseau Ethereum {#connect-to-ethereum} -Il existe de nombreuses façons d'émettre des requêtes sur la blockchain Ethereum, mais pour faciliter les choses, nous utiliserons un compte gratuit sur [Alchemy](https://alchemy.com/signup/eth), une plateforme pour développeurs de blockchain et une API nous permettant de communiquer avec la chaîne Ethereum sans avoir à exécuter nos propres nœuds. +Il existe de nombreuses façons de faire des requêtes à la blockchain Ethereum, mais pour simplifier les choses, nous utiliserons un compte gratuit sur [Alchemy](https://alchemy.com/signup/eth), une plateforme de développement blockchain et une API qui nous permet de communiquer avec la chaîne Ethereum sans avoir à exécuter nos propres nœuds. Dans ce tutoriel, nous allons également tirer parti des outils de développement d'Alchemy pour le suivi et l'analyse afin de comprendre ce qui se passe sous le capot concernant le déploiement de notre contract intelligent. Si vous n'avez pas encore de compte Alchemy, vous pouvez vous inscrire gratuitement [ici](https://alchemy.com/signup/eth). -## Étape 2 : Créer votre application (et votre clé d'API) {#make-api-key} +## Étape 2 : Créer votre application (et votre clé API) {#make-api-key} -Une fois que vous avez créé un compte Alchemy, vous pouvez générer une clé d'API en créant une application. Cela va nous permettre d'émettre des requêtes sur le réseau de test Sepolia. Consultez [ce guide](https://docs.alchemyapi.io/guides/choosing-a-network), si vous voulez en apprendre plus sur les réseaux de test. +Une fois votre compte Alchemy créé, vous pouvez générer une clé API en créant une application. Cela va nous permettre d'émettre des requêtes sur le réseau de test Sepolia. Consultez [ce guide](https://docs.alchemyapi.io/guides/choosing-a-network) si vous souhaitez en savoir plus sur les réseaux de test. 1. Accédez à la page « Create App » dans votre Tableau de bord Alchemy, en survolant « Apps » dans la barre de navigation et en cliquant sur « Create App » -![Créez votre application](./create-your-app.png) +![Créer votre application](./create-your-app.png) 2. Nommez votre application (nous avons choisi « Mon premier NFT ! »), donnez une brève description, sélectionnez « Ethereum » pour la chaine, et choisissez « Sepolia » pour votre réseau. Depuis la fusion, les autres réseaux de test sont obsolètes. -![Configurez et publiez votre application](./alchemy-explorer-sepolia.png) +![Configurer et publier votre application](./alchemy-explorer-sepolia.png) -3. Cliquez sur « Create App » et voilà ! Votre application devrait apparaître dans le tableau ci-dessous. +3. Cliquez sur « Create app », et voilà ! Votre application devrait apparaître dans le tableau ci-dessous. -## Étape 3 : Créer un compte Ethereum (une adresse) {#create-eth-address} +## Étape 3 : Créer un compte Ethereum (adresse) {#create-eth-address} -Nous avons besoin d'un compte Ethereum pour effectuer des transactions (envoyer et recevoir). Pour ce tutoriel, nous utiliserons MetaMask, un portefeuille virtuel utilisable dans le navigateur servant à gérer les adresses Ethereum. Si vous voulez en savoir plus sur le fonctionnement des transactions sur Ethereum, consultez [cette page](/developers/docs/transactions/) de la fondation Ethereum. +Nous avons besoin d'un compte Ethereum pour effectuer des transactions (envoyer et recevoir). Pour ce tutoriel, nous allons utiliser MetaMask, un portefeuille virtuel intégré au navigateur, servant à gérer les adresses de votre compte Ethereum. Si vous voulez mieux comprendre le fonctionnement des transactions sur Ethereum, consultez [cette page](/developers/docs/transactions/) de la Fondation Ethereum. Vous pouvez télécharger et créer un compte MetaMask gratuitement [ici](https://metamask.io/download). Lorsque vous créez un compte, ou si vous en avez déjà un, assurez-vous de basculer sur « Réseau de test Sepolia » en haut à droite (afin de ne pas utiliser d'argent réel). ![Définir Sepolia comme votre réseau](./metamask-goerli.png) -## Étape 4 : Ajouter des ethers depuis un faucet {#step-4-add-ether-from-a-faucet} +## Étape 4 : Ajouter de l'ether à partir d'un robinet {#step-4-add-ether-from-a-faucet} -Afin de déployer notre contrat intelligent sur le réseau de test, nous aurons besoin de faux ETH. Pour obtenir l'ETH, vous pouvez vous rendre sur le [Robinet Sepolia](https://sepoliafaucet.com/) hébergé par Alchemy, vous connecter et entrer l'adresse de votre compte, puis cliquez sur « Envoyez-moi des ETH ». Vous devriez voir les ETH sur votre compte MetaMask rapidement après ! +Afin de déployer notre contrat intelligent sur le serveur test, nous aurons besoin de faux ETH. Pour obtenir des ETH, vous pouvez vous rendre sur le [robinet Sepolia](https://sepoliafaucet.com/) hébergé par Alchemy, vous connecter et entrer l'adresse de votre compte, puis cliquer sur « Send Me ETH ». Vous devriez voir votre ETH dans votre compte MetaMask peu de temps après ! -## Étape 5 : Vérifiez votre solde {#check-balance} +## Étape 5 : Vérifier votre solde {#check-balance} -Pour vérifier notre solde, faisons une requête [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) en utilisant [l'outil de composition d'Alchemy](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Cela va renvoyer la quantité d'ETH dans notre portefeuille. Après avoir entré l'adresse de votre compte MetaMask et cliqué sur « Send Request », vous devriez voir une réponse comme celle-ci : +Pour vérifier que notre solde est bien là, faisons une requête [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) en utilisant l'[outil de composition d'Alchemy](https://composer.alchemyapi.io?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Cela va renvoyer la quantité d'ETH dans notre portefeuille. Après avoir entré l'adresse de votre compte MetaMask et cliqué sur « Send Request », vous devriez voir une réponse comme celle-ci : - `{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}` + ``` + {"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} + ``` -> **Remarque** Ce résultat est en wei et non pas en ETH. Le wei est utilisé comme la plus petite dénomination d'ether. La conversion entre wei et ETH est 1 eth = 1018 wei. Donc si on convertit 0xde0b6b3a7640000 en décimale, nous obtenons 1\*1018 wei, ce qui équivaut à 1 ETH. +> **Remarque :** ce résultat est en wei, pas en ETH. Le wei est utilisé comme la plus petite dénomination d'ether. La conversion entre wei et ETH est 1 eth = 1018 wei. Donc si on convertit 0xde0b6b3a7640000 en décimale, nous obtenons 1\*1018 wei, ce qui équivaut à 1 ETH. Ouf ! Notre faux argent est bien là. -## Étape 6 : Initialisons notre projet {#initialize-project} +## Étape 6 : Initialiser notre projet {#initialize-project} Pour commencer, nous allons devoir créer un dossier pour notre projet. Ouvrez votre ligne de commande et tapez : + ``` mkdir my-nft cd my-nft + ``` -Maintenant que nous sommes dans le dossier de notre projet, nous allons utiliser npm init pour initialiser le projet. Si vous n'avez pas encore installé npm, suivez [ces instructions](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (nous allons également avoir besoin de [Node.js](https://nodejs.org/en/download/); donc téléchargez-le aussi !). +Maintenant que nous sommes dans le dossier de notre projet, nous allons utiliser npm init pour initialiser le projet. Si vous n'avez pas encore installé npm, suivez [ces instructions](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (nous aurons également besoin de [Node.js](https://nodejs.org/en/download/), alors téléchargez-le aussi !). + ``` npm init + ``` La manière dont vous répondez à ces questions d'installation a peu d'importance ; pour référence, voici comment nous avons répondu : + ```json package name: (my-nft) version: (1.0.0) - description: My first NFT! + description: Mon premier NFT ! entry point: (index.js) test command: git repository: @@ -91,7 +100,7 @@ La manière dont vous répondez à ces questions d'installation a peu d'importan { "name": "my-nft", "version": "1.0.0", - "description": "My first NFT!", + "description": "Mon premier NFT !", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -100,6 +109,7 @@ La manière dont vous répondez à ces questions d'installation a peu d'importan "license": "ISC" } ``` + Approuvez le package.json, et nous sommes prêts à démarrer ! ## Étape 7 : Installer [Hardhat](https://hardhat.org/getting-started/#overview) {#install-hardhat} @@ -108,18 +118,23 @@ Hardhat est un environnement de développement qui permet de compiler, déployer Dans notre projet my-nft, exécutez : + ``` npm install --save-dev hardhat + ``` -Consultez cette page pour plus de détails sur [les instructions d'installation](https://hardhat.org/getting-started/#overview). +Consultez cette page pour plus de détails sur les [instructions d'installation](https://hardhat.org/getting-started/#overview). -## Étape 8 : Créer un projet Hardhat {#create-hardhat-project} +## Étape 8 : Créer le projet Hardhat {#create-hardhat-project} Dans notre dossier de projet, exécutez : + ``` npx hardhat + ``` -Vous devriez maintenant voir un message de bienvenue ainsi qu'une option pour séléctionner ce que vous voulez faire. Sélectionnez : « create an empty hardhat.config.js » : +Vous devriez maintenant voir un message de bienvenue ainsi qu'une option pour sélectionner ce que vous voulez faire. Sélectionnez : « create an empty hardhat.config.js » : + ``` 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 @@ -128,20 +143,23 @@ Vous devriez maintenant voir un message de bienvenue ainsi qu'une option pour s 888 888 .d888888 888 888 888 888 888 .d888888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 - 👷 Welcome to Hardhat v2.0.11 👷‍ - ? Que voulez vous faire ? … - Create a sample project - ❯ Create an empty hardhat.config.js - Quit + 👷 Bienvenue dans Hardhat v2.0.11 👷‍ + ? Que voulez-vous faire ? … + Créer un projet d'exemple + ❯ Créer un fichier hardhat.config.js vide + Quitter + ``` Cela va générer un fichier 'hardhar.config.js' dans lequel nous allons spécifier tous les paramètres de notre projet (à l'étape 13). ## Étape 9 : Ajouter les dossiers du projet {#add-project-folders} -Pour garder notre projet organisé, nous allons créer deux nouveaux dossiers. Naviguez vers le répertoire racine de votre projet dans votre ligne de commande et tapez : +Pour garder notre projet organisé, nous allons créer deux nouveaux dossiers. Naviguez vers le répertoire racine de votre projet dans votre invite de commande en ligne et tapez : + ``` mkdir contracts mkdir scripts + ``` - contracts/ est l'endroit où nous garderons notre code de contrat intelligent NFT @@ -149,16 +167,16 @@ Pour garder notre projet organisé, nous allons créer deux nouveaux dossiers. N ## Étape 10 : Écrire notre contrat {#write-contract} -Maintenant que notre environnement est configuré, passons aux choses plus excitantes : _écrire le code de notre contrat intelligent !_ +Maintenant que notre environnement est configuré, passons à des choses plus passionnantes : _l'écriture du code de notre contrat intelligent !_ -Ouvrez le projet my-nft dans votre éditeur de code favori (nous apprécions [VSCode](https://code.visualstudio.com/)). Les contrats intelligents sont écrits dans un langage appelé Solidity, qui est celui que nous allons utiliser pour écrire notre contrat intelligent MyNFT.sol. +Ouvrez le projet my-nft dans votre éditeur préféré (nous aimons [VSCode](https://code.visualstudio.com/)). Les contrats intelligents sont écrits dans un langage appelé Solidity, qui est celui que nous allons utiliser pour écrire notre contrat intelligent MyNFT.sol. -1. Naviguez vers le dossier `contracts` et créez un nouveau fichier appelé MyNFT.sol +1. Allez dans le dossier `contracts` et créez un nouveau fichier nommé MyNFT.sol -2. Ci-dessous vous trouverez le code de notre contrat intelligent NFT, que nous avons basé sur l'implémentation ERC-721 de la bibliothèque [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721). Copiez et collez le contenu ci-dessous dans votre fichier MyNFT.sol. +2. Vous trouverez ci-dessous le code de notre contrat intelligent NFT, que nous avons basé sur l'implémentation ERC-721 de la bibliothèque [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721). Copiez et collez le contenu ci-dessous dans votre fichier MyNFT.sol. ```solidity - //Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) + // Contrat basé sur [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721) // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -188,54 +206,58 @@ Ouvrez le projet my-nft dans votre éditeur de code favori (nous apprécions [VS } ``` -3. Comme nous héritons des classes de la bibliothèque de contrats OpenZeppelin, dans votre ligne de commande, exécutez `npm install @openzeppelin/contracts` pour installer la bibliothèque dans notre dossier. +3. Puisque nous héritons des classes de la bibliothèque de contrats OpenZeppelin, exécutez `npm install @openzeppelin/contracts^4.0.0` dans votre ligne de commande pour installer la bibliothèque dans notre dossier. -Que _fait_ donc exactement ce code ? Décortiquons-le ligne par ligne. +Alors, que _fait_ ce code exactement ? Décortiquons-le ligne par ligne. -En haut de notre contrat intelligent, nous importons trois classes des contrats intelligents d'[OpenZeppelin](https://openzeppelin.com/) : +En haut de notre contrat intelligent, nous importons trois classes de contrats intelligents [OpenZeppelin](https://openzeppelin.com/) : -- @openzeppelin/contracts/token/ERC721/ERC721.sol contient l'implémentation de la norme ERC-721, dont notre contrat intelligent NFT héritera. (Pour être un NFT valide, votre contrat intelligent doit implémenter toutes les méthodes de la norme ERC-721.) Pour en savoir plus sur les fonctions héritées d'ERC-721, consultez la définition de l'interface [ici](https://eips.ethereum.org/EIPS/eip-721). +- @openzeppelin/contracts/token/ERC721/ERC721.sol contient l'implémentation de la norme ERC-721, dont notre contrat intelligent NFT héritera. (Pour être un NFT valide, votre contrat intelligent doit implémenter toutes les méthodes de la norme ERC-721.) Pour en savoir plus sur les fonctions ERC-721 héritées, consultez la définition de l'interface [ici](https://eips.ethereum.org/EIPS/eip-721). - @openzeppelin/contracts/utils/Counters.sol fournit des compteurs qui ne peuvent être incrémentés ou décrémentés que de un. Notre contrat intelligent utilise des compteurs pour garder une trace du nombre total de NFT créés et définir l'ID unique de votre nouveau NFT. (Chaque NFT créé à l'aide d'un contrat intelligent doit se voir attribuer un ID unique - ici notre ID unique est simplement déterminé par le nombre total de NFT existants. Par exemple, le premier NFT que nous créons avec notre contrat intelligent a un ID de « 1 », le deuxième NFT a un ID de « 2 », etc.) -- @openzeppelin/contracts/access/Ownable.sol met en place [un contrôle d'accès](https://docs.openzeppelin.com/contracts/3.x/access-control) sur notre contrat intelligent, de sorte que seul le propriétaire du contrat intelligent (vous) puisse créer des NFT. (Remarque : l'inclusion du contrôle d'accès est entièrement une préférence. Si vous souhaitez que n'importe qui puisse créer un NFT en utilisant votre contrat intelligent, supprimez le mot « Ownable » à la ligne 10 et « onlyOwner » à la ligne 17.) +- @openzeppelin/contracts/access/Ownable.sol met en place un [contrôle d'accès](https://docs.openzeppelin.com/contracts/3.x/access-control) sur notre contrat intelligent, de sorte que seul le propriétaire du contrat intelligent (vous) puisse frapper des NFT. (Remarque : l'inclusion du contrôle d'accès est entièrement une préférence. Si vous souhaitez que n'importe qui puisse créer un NFT en utilisant votre contrat intelligent, supprimez le mot « Ownable » à la ligne 10 et « onlyOwner » à la ligne 17.) -Après nos déclarations d'importation, nous obtenons notre contrat intelligent NFT personnalisé, qui est étonnamment court - il ne contient qu'un compteur, un constructeur et une seule fonction ! Ceci grâce à nos contrats OpenZeppelin hérités, qui mettent en œuvre la plupart des méthodes dont nous avons besoin pour créer un NFT, comme `ownerOf` qui renvoie le propriétaire du NFT, et `transferFrom`, qui transfère la propriété du NFT d'un compte à un autre. +Après nos déclarations d'importation, nous obtenons notre contrat intelligent NFT personnalisé, qui est étonnamment court - il ne contient qu'un compteur, un constructeur et une seule fonction ! C'est grâce à nos contrats OpenZeppelin hérités, qui implémentent la plupart des méthodes dont nous avons besoin pour créer un NFT, comme `ownerOf` qui renvoie le propriétaire du NFT, et `transferFrom`, qui transfère la propriété du NFT d'un compte à un autre. Dans notre constructeur ERC-721, vous remarquerez que nous passons deux chaînes de caractères, « MyNFT » et « NFT ». La première variable est le nom de notre contrat intelligent, et le second est son symbole. Vous pouvez nommer chacune de ces variables comme vous le souhaitez ! -Enfin, nous avons notre fonction `mintNFT (destinataire de l'adresse, string memory tokenURI)` qui nous permet de frapper un NFT ! Vous remarquerez que cette fonction prend en paramètre deux variables : +Enfin, nous avons notre fonction `mintNFT(address recipient, string memory tokenURI)` qui nous permet de frapper un NFT ! Vous remarquerez que cette fonction prend en paramètre deux variables : -- `address recipient` spécifie l'addresse qui recevra votre NFT fraîchement créé +- `address recipient` spécifie l'adresse qui recevra votre NFT fraîchement frappé. -- `string memory tokenURI` est une chaîne de caractères qui doit se résoudre en un document JSON décrivant les métadonnées du NFT. Les métadonnées d'un NFT sont ce qui lui donne vie, lui permettant d'avoir des propriétés configurables, comme un nom, une description, une image et d'autres attributs. Dans la deuxième partie de ce tutoriel, nous décrirons comment configurer ces métadonnées. +- `string memory tokenURI` est une chaîne de caractères qui doit correspondre à un document JSON décrivant les métadonnées du NFT. Les métadonnées d'un NFT sont ce qui lui donne vie, lui permettant d'avoir des propriétés configurables, comme un nom, une description, une image et d'autres attributs. Dans la deuxième partie de ce tutoriel, nous décrirons comment configurer ces métadonnées. `mintNFT` appelle certaines méthodes de la bibliothèque ERC-721 héritée, et renvoie finalement un nombre qui représente l'ID du NFT fraîchement frappé. -## Étape 11 : Connecter MetaMask & Alchemy à votre projet {#connect-metamask-and-alchemy} +## Étape 11 : Connecter MetaMask et Alchemy à votre projet {#connect-metamask-and-alchemy} Maintenant que nous avons créé un portefeuille MetaMask, un compte Alchemy et écrit notre contrat intelligent, il est temps de connecter les trois. Chaque transaction envoyée depuis votre portefeuille virtuel nécessite une signature en utilisant votre clé privée unique. Pour donner cette permission à notre programme, nous pouvons stocker en toute sécurité notre clé privée (et la clé API Alchemy) dans un fichier d'environnement. -Pour en savoir plus sur l'envoi de transactions, consultez [ce tutoriel](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) sur l'envoi de transactions avec web3. +Pour en savoir plus sur l'envoi de transactions, consultez [ce tutoriel](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) sur l'envoi de transactions à l'aide de web3. Premièrement, installez le paquet dotenv dans votre dossier de projet : + ``` npm install dotenv --save + ``` -Ensuite, créez un fichier `.env` dans le répertoire racine de notre projet et ajoutez-y votre clé privée MetaMask et l'URL de l'API HTTP Alchemy. +Ensuite, créez un fichier `.env` à la racine de notre projet, et ajoutez-y votre clé privée MetaMask et votre URL d'API HTTP Alchemy. -- Suivez [ces instructions](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key) pour exporter votre clé privée de MetaMask +- Suivez [ces instructions](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key) pour exporter votre clé privée depuis MetaMask. - Voir ci-dessous pour obtenir l'URL de l'API HTTP Alchemy et la copier dans votre presse-papiers -![Copiez l'URL de votre API Alchemy](./copy-alchemy-api-url.gif) +![Copier votre URL d'API Alchemy](./copy-alchemy-api-url.gif) -Votre fichier `.env` devrait ressembler à ceci : +Votre fichier `.env` devrait maintenant ressembler à ceci : + ``` API_URL="https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY="your-metamask-private-key" + ``` Pour les relier effectivement à notre code, nous ferons référence à ces variables dans notre fichier hardhat.config.js à l'étape 13. @@ -243,13 +265,15 @@ Pour les relier effectivement à notre code, nous ferons référence à ces vari ## Étape 12 : Installer Ethers.js {#install-ethers} -Ethers.js est une bibliothèque qui permet facilement d'interagir et de faire des requêtes pour Ethereum en enveloppant les méthodes [standard JSON-RPC](/developers/docs/apis/json-rpc/) avec des méthodes plus conviviales d'utilisation. +Ethers.js est une bibliothèque qui facilite l'interaction et l'envoi de requêtes à Ethereum en encapsulant les [méthodes JSON-RPC standard](/developers/docs/apis/json-rpc/) dans des méthodes plus conviviales. -Hardhat facilite grandement l'intégration de [Plugins](https://hardhat.org/plugins/) pour des outils supplémentaires et des fonctionnalités étendues. Nous profiterons du [plugin Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) pour le déploiement de contrats ([Ethers.js](https://github.com/ethers-io/ethers.js/) dispose de quelques méthodes très nettes de déploiement de contrat). +Hardhat facilite grandement l'intégration de [plugins](https://hardhat.org/plugins/) pour des outils supplémentaires et des fonctionnalités étendues. Nous allons profiter du [plugin Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) pour le déploiement du contrat ([Ethers.js](https://github.com/ethers-io/ethers.js/) a des méthodes de déploiement de contrat très propres). Dans votre dossier de projet, tapez : + ``` npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.0.0 + ``` Nous aurons également besoin d'ethers dans notre hardhat.config.js à l'étape suivante. @@ -285,24 +309,26 @@ Pour s’assurer à ce stade que tout fonctionne, compilons notre contrat. La t À partir de la ligne de commande, exécutez : + ``` npx hardhat compile + ``` -Vous pourriez voir un avertissement du type « SPDX license identifier not provided in source file », mais nul besoin de vous inquiéter — espérons que tout le reste fonctionne ! Si ce n'est pas le cas, vous pouvez toujours envoyer un message dans le Discord [Alchemy](https://discord.gg/u72VCg3). +Vous pourriez voir un avertissement du type « SPDX license identifier not provided in source file » (identifiant de licence SDPX non fourni dans le fichier source), mais nul besoin de vous inquiéter — espérons que tout le reste fonctionne ! Sinon, vous pouvez toujours envoyer un message sur le [Discord d'Alchemy](https://discord.gg/u72VCg3). ## Étape 15 : Écrire notre script de déploiement {#write-deploy} Maintenant que notre contrat est codé et que notre fichier de configuration est bon, il est temps d’écrire notre script de déploiement pour notre contrat. -Naviguez vers le dossier `scripts/` et créez un nouveau fichier appelé `deploy.js`, en y ajoutant le contenu suivant : +Allez dans le dossier `scripts/`, créez un nouveau fichier nommé `deploy.js` et ajoutez-y le contenu suivant : ```js async function main() { const MyNFT = await ethers.getContractFactory("MyNFT") - // Start deployment, returning a promise that resolves to a contract object + // Démarrer le déploiement, en retournant une promesse qui se résout en un objet de contrat const myNFT = await MyNFT.deploy() await myNFT.deployed() - console.log("Contract deployed to address:", myNFT.address) + console.log("Contrat déployé à l'adresse :", myNFT.address) } main() @@ -313,40 +339,48 @@ main() }) ``` -Hardhat est incroyable en ce sens qu'il explique clairement ce que fait chacune des lignes de code au travers de son [tutoriel sur les contrats](https://hardhat.org/tutorial/testing-contracts.html#writing-tests). Nous avons repris ces explications ici. +Hardhat explique très bien ce que fait chacune de ces lignes de code dans son [tutoriel sur les contrats](https://hardhat.org/tutorial/testing-contracts.html#writing-tests), nous avons repris leurs explications ici. + ``` const MyNFT = await ethers.getContractFactory("MyNFT"); + ``` Un ContractFactory dans ethers.js est une abstraction utilisée pour déployer de nouveaux contrats intelligents, donc MyNFT est ici une usine pour les instances de notre contrat NFT. Lors de l'utilisation du plugin « hardhat-ethers », les instances de ContractFactory et de Contract sont connectées au premier signataire par défaut. + ``` const myNFT = await MyNFT.deploy(); + ``` -L'appel de deploy() sur un ContractFactory lancera le déploiement, et renverra une « Promise » qui se résout en un Contrat. C'est l'objet qui a une méthode pour chacune de nos fonctions de contrat intelligent. +L'appel de deploy() sur un ContractFactory lancera le déploiement, et renverra une « Promise » qui se résout en un Contrat. C'est l'objet qui possède une méthode pour chacune des fonctions de notre contrat intelligent. ## Étape 16 : Déployer notre contrat {#deploy-contract} Nous sommes enfin prêts à déployer notre contrat intelligent ! Naviguez à nouveau vers la racine du répertoire de votre projet, et dans la ligne de commande, exécutez : + ``` npx hardhat --network sepolia run scripts/deploy.js + ``` -Vous devriez dès lors voir quelque chose comme : +Vous devriez maintenant voir quelque chose comme : + ``` Contrat déployé à l'adresse : 0x4C5266cCc4b3F426965d2f51b6D910325a0E7650 + ``` -Si nous allons sur l'[etherscan Sepolia](https://sepolia.etherscan.io/) et que nous recherchons l'adresse de notre contrat, nous devrions constater qu'il a été déployé avec succès. Si vous ne pouvez pas le voir immédiatement, veuillez patienter, car cela peut prendre un certain temps. La transaction ressemblera à ceci : +Si nous allons sur l'[Etherscan de Sepolia](https://sepolia.etherscan.io/) et que nous recherchons l'adresse de notre contrat, nous devrions pouvoir voir qu'il a été déployé avec succès. Si vous ne pouvez pas le voir immédiatement, veuillez patienter, car cela peut prendre un certain temps. La transaction ressemblera à quelque chose comme : -![Consultez votre adresse de transaction sur Etherscan](./etherscan-sepoila-contract-creation.png) +![Afficher l'adresse de votre transaction sur Etherscan](./etherscan-sepoila-contract-creation.png) -L'adresse « From » doit correspondre à l'adresse de votre compte MetaMask et l'adresse « To » doit indiquer « Création de contrat ». Si nous cliquons sur la transaction, nous verrons l'adresse de notre contrat dans le champ « To » : +L'adresse de l'expéditeur (From) doit correspondre à l'adresse de votre compte MetaMask et l'adresse du destinataire (To) indiquera « Création de contrat ». Si nous cliquons sur la transaction, nous verrons l'adresse de notre contrat dans le champ « To » : -![Consultez l'adresse de votre contrat sur Etherscan](./etherscan-sepolia-tx-details.png) +![Afficher l'adresse de votre contrat sur Etherscan](./etherscan-sepolia-tx-details.png) Super ! Vous venez de déployer votre contrat intelligent NFT sur la chaîne (réseau de test) d'Ethereum ! -Pour comprendre ce qui se passe sous le capot, naviguons dans l'onglet Explorer de notre [tableau de bord Alchemy](https://dashboard.alchemyapi.io/explorer). Si vous avez plusieurs applications Alchemy, veillez à filtrer par application et à sélectionner « MyNFT ». +Pour comprendre ce qui se passe en coulisses, allons dans l'onglet Explorer de notre [tableau de bord Alchemy](https://dashboard.alchemyapi.io/explorer). Si vous avez plusieurs applications Alchemy, veillez à filtrer par application et à sélectionner « MyNFT ». -![Visualisez les appels effectués « sous le capot » avec le tableau de bord Explorer d'Alchemy](./alchemy-explorer-goerli.png) +![Afficher les appels effectués « en coulisses » avec le tableau de bord Explorer d'Alchemy](./alchemy-explorer-goerli.png) -Vous verrez ici un certain nombre d'appels JSON-RPC qu'Hardhat/Ethers ont effectué sous le capot pour nous lorsque nous avons appelé la fonction .deploy(). Les deux plus importants sont [eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction), qui est la requête pour écrire réellement notre contrat intelligent sur la chaîne Sepolia, et [eth_getTransactionByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash) qui est une requête pour lire des informations sur notre transaction étant donné le hachage (un modèle type lors de l'envoi de transactions). Pour en savoir plus sur l'envoi de transactions, consultez ce tutoriel sur [l'envoi de transactions à l'aide de Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). +Vous verrez ici un certain nombre d'appels JSON-RPC qu'Hardhat/Ethers ont effectué sous le capot pour nous lorsque nous avons appelé la fonction .deploy(). Deux appels importants à signaler ici sont [eth_sendRawTransaction](/developers/docs/apis/json-rpc/#eth_sendrawtransaction), qui est la requête pour effectivement écrire notre contrat intelligent sur la chaîne Sepolia, et [eth_getTransactionByHash](/developers/docs/apis/json-rpc/#eth_gettransactionbyhash) qui est une requête pour lire des informations sur notre transaction à partir de son hachage (un modèle typique lors de l'envoi de transactions). Pour en savoir plus sur l'envoi de transactions, consultez ce tutoriel sur l'[envoi de transactions à l'aide de Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). -C'est tout pour la Partie 1 de ce tutoriel. Dans la [Partie 2, nous interagirons réellement avec notre contrat intelligent en créant notre NFT](/developers/tutorials/how-to-mint-an-nft/), et dans la [Partie 3, nous vous montrerons comment visualiser votre NFT dans votre portefeuille Ethereum](/developers/tutorials/how-to-view-nft-in-metamask/) ! +C'est tout pour la Partie 1 de ce tutoriel. Dans la [partie 2, nous interagirons avec notre contrat intelligent en frappant un NFT](/developers/tutorials/how-to-mint-an-nft/), et dans la [partie 3, nous vous montrerons comment voir votre NFT dans votre portefeuille Ethereum](/developers/tutorials/how-to-view-nft-in-metamask/) ! diff --git a/public/content/translations/fr/developers/tutorials/interact-with-other-contracts-from-solidity/index.md b/public/content/translations/fr/developers/tutorials/interact-with-other-contracts-from-solidity/index.md index aa983a23a23..0486be3ab1d 100644 --- a/public/content/translations/fr/developers/tutorials/interact-with-other-contracts-from-solidity/index.md +++ b/public/content/translations/fr/developers/tutorials/interact-with-other-contracts-from-solidity/index.md @@ -1,13 +1,15 @@ --- -title: Interagir avec les autres contrats de Solidity -description: Comment déployer un contrat intelligent à partir d'un contrat existant et interagir avec lui +title: Interagir avec d'autres contrats depuis Solidity +description: "Comment déployer un contrat intelligent à partir d'un contrat existant et interagir avec lui" author: "jdourlens" tags: - - "contrats intelligents" - - "solidity" - - "remix" - - "déploiement" - - "composabilité" + [ + "contrats intelligents", + "solidité", + "remix", + "déploiement", + "composabilité" + ] skill: advanced lang: fr published: 2020-04-05 @@ -16,9 +18,9 @@ sourceUrl: https://ethereumdev.io/interact-with-other-contracts-from-solidity/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -Dans le tutoriel précédent nous avons appris [Comment déployer votre premier contrat intelligent](/developers/tutorials/deploying-your-first-smart-contract/) et ajouter des fonctionnalité comme [le contrôle d'accès avec des modificateurs](https://ethereumdev.io/organize-your-code-and-control-access-to-your-smart-contract-with-modifiers/) ou [la gestion d'erreur avec Solidity](https://ethereumdev.io/handle-errors-in-solidity-with-require-and-revert/). Dans ce tutoriel, nous allons apprendre comment déployer un contrat intelligent à partir d'un contrat existant et interagir avec celui-ci. +Dans les tutoriels précédents, nous avons beaucoup appris sur [comment déployer votre premier contrat intelligent](/developers/tutorials/deploying-your-first-smart-contract/), comment y ajouter certaines fonctionnalités comme le [contrôle d'accès avec des modificateurs](https://ethereumdev.io/organize-your-code-and-control-access-to-your-smart-contract-with-modifiers/) ou la [gestion des erreurs dans Solidity](https://ethereumdev.io/handle-errors-in-solidity-with-require-and-revert/). Dans ce tutoriel, nous allons apprendre comment déployer un contrat intelligent à partir d'un contrat existant et interagir avec celui-ci. -Nous allons créer un contrat qui permet à quiconque de disposer de son propre contrat intelligent `Counter` en créant une usine. Nous l'appellerons `CounterFactory`. Tout d'abord voici le code de notre précèdent contrat intelligent `Counter` : +Nous allons créer un contrat qui permet à quiconque d'avoir son propre contrat intelligent `Counter` en créant une fabrique pour celui-ci. Son nom sera `CounterFactory`. Voici d'abord le code de notre contrat intelligent `Counter` initial : ```solidity pragma solidity 0.5.17; @@ -31,12 +33,12 @@ contract Counter { modifier onlyOwner(address caller) { - require(caller == _owner, "Vous le proprietaire du contrat"); + require(caller == _owner, "Vous n'êtes pas le propriétaire du contrat"); _; } modifier onlyFactory() { - require(msg.sender == _factory, "Vous avez besoin d’un factory"); + require(msg.sender == _factory, "Vous devez utiliser la fabrique"); _; } @@ -56,19 +58,19 @@ contract Counter { } ``` -Notez que nous avons légèrement modifié le code du contrat pour garder une trace de l'adresse de la Factory et de l'adresse du titulaire du contrat. Lorsque vous appelez un code contrat depuis un autre contrat, msg.render se réfère à l'adresse de notre contrat Factory. C'est **un point vraiment important à comprendre** car utiliser un contrat pour interagir avec d'autres contrats est une pratique courante. Il convient donc de déterminer qui est l'expéditeur dans des cas complexes. +Notez que nous avons légèrement modifié le code du contrat pour garder une trace de l'adresse de la fabrique et de l'adresse du propriétaire du contrat. Lorsque vous appelez un code de contrat depuis un autre contrat, `msg.sender` fera référence à l'adresse de notre fabrique de contrats. C'est un **point vraiment important à comprendre**, car l'utilisation d'un contrat pour interagir avec d'autres contrats est une pratique courante. Vous devez donc faire attention à qui est l'expéditeur dans les cas complexes. -Pour cela, nous avons également ajouté un modificateur `onlyFactory` qui s'assure que la fonction de changement d'état ne peut être appelée que par la Factory qui passera l'appelant original comme paramètre. +Pour cela, nous avons également ajouté un modificateur `onlyFactory` qui s'assure que la fonction de changement d'état ne peut être appelée que par la fabrique qui transmettra l'appelant original en tant que paramètre. -À l'intérieur de notre nouvelle `CounterFactory` qui gérera tous les autres contre-contrats, nous ajouterons un mapping qui associera un titulaire à l'adresse de son contrat: +À l'intérieur de notre nouvelle `CounterFactory` qui gérera tous les autres contrats Compteur, nous ajouterons un mapping qui associera un propriétaire à l'adresse de son contrat Compteur : ```solidity mapping(address => Counter) _counters; ``` -Dans Ethereum, le mapping est équivalent aux objets en javascript, il permet de faire correspondre une clé de type A à une valeur de type B. Dans ce cas, nous cartographions l'adresse d'un propriétaire avec l'instance de son contre-contrat. +Dans Ethereum, les mappings sont l'équivalent des objets en JavaScript. Ils permettent de mapper une clé de type A à une valeur de type B. Dans ce cas, nous mappons l'adresse d'un propriétaire avec l'instance de son Compteur. -Instancier un nouveau contre-contrat pour quelqu'un ressemblera à ceci : +Instancier un nouveau Compteur pour quelqu'un ressemblera à ceci : ```solidity function createCounter() public { @@ -77,9 +79,9 @@ Instancier un nouveau contre-contrat pour quelqu'un ressemblera à ceci : } ``` -Nous vérifions d'abord si la personne est déjà propriétaire d'un contre-contrat. S'il ne possède pas de contre-contrat, nous instancions un nouveau contre-contrat en passant son adresse au constructeur `Counter` et assignons l'instance nouvellement créée au mapping. +Nous vérifions d'abord si la personne possède déjà un Compteur. S'il ne possède pas de Compteur, nous instancions un nouveau Compteur en passant son adresse au constructeur `Counter` et nous assignons l'instance nouvellement créée au mapping. -Pour obtenir le nombre de vues d'un contre-contrat spécifique, il faudra faire comme ceci : +Pour obtenir la valeur d'un Compteur spécifique, cela ressemblera à ceci : ```solidity function getCount(address account) public view returns (uint256) { @@ -92,9 +94,9 @@ function getMyCount() public view returns (uint256) { } ``` -La première fonction vérifie s'il existe un contre-contrat pour une adresse donnée, puis appelle la méthode `getCount` de l'instance. La deuxième fonction : `getMyCount` est juste une courte opération pour passer le msg.sender directement à la fonction `getCount`. +La première fonction vérifie si le contrat Compteur existe pour une adresse donnée, puis appelle la méthode `getCount` depuis l'instance. La deuxième fonction, `getMyCount`, est juste un raccourci pour passer directement `msg.sender` à la fonction `getCount`. -La fonction `increment` est assez similaire mais bascule l'émetteur de la transaction originale vers le contrat `Counter`: +La fonction `increment` est assez similaire, mais elle transmet l'expéditeur de la transaction originale au contrat `Counter` : ```solidity function increment() public { @@ -103,11 +105,11 @@ function increment() public { } ``` -Notez que s'il est trop sollicité, notre compteur pourrait être saturé. Il convient d'utiliser la bibliothèque [SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/) autant que possible pour se protéger de cette éventualité. +Notez que s'il est appelé trop de fois, notre compteur pourrait être victime d'un dépassement de capacité (overflow). Vous devriez utiliser la [bibliothèque SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/) autant que possible pour vous protéger de ce cas de figure. -Pour déployer notre contrat, vous devrez fournir à la fois le code de la `CounterFactory` et du `Counter`. Lors du déploiement, dans Remix par exemple, vous devrez sélectionner CounterFactory. +Pour déployer notre contrat, vous devrez fournir à la fois le code de `CounterFactory` et de `Counter`. Lors du déploiement, dans Remix par exemple, vous devrez sélectionner CounterFactory. -Voici le code final : +Voici le code complet : ```solidity pragma solidity 0.5.17; @@ -120,12 +122,12 @@ contract Counter { modifier onlyOwner(address caller) { - require(caller == _owner, "Vous etes le proprietaire du contrat"); + require(caller == _owner, "Vous n'êtes pas le propriétaire du contrat"); _; } modifier onlyFactory() { - require(msg.sender == _factory, "Vous avez besoin d’un factory"); + require(msg.sender == _factory, "Vous devez utiliser la fabrique"); _; } @@ -170,8 +172,8 @@ contract CounterFactory { } ``` -Après avoir compilé, dans la section de déploiement de Remix, vous sélectionnez la Factory à déployer : +Après compilation, dans la section de déploiement de Remix, vous sélectionnerez la fabrique à déployer : -![Sélection de la Factory à déployer dans Remix](./counterfactory-deploy.png) +![Sélection de la fabrique à déployer dans Remix](./counterfactory-deploy.png) -Ensuite, vous pouvez jouer avec votre Factory à contrat et suivre l'évolution de la valeur. Si vous souhaitez appeler le contrat intelligent à partir d'une adresse différente, vous devrez changer l'adresse dans la sélection de compte de Remix. +Ensuite, vous pouvez vous amuser avec votre fabrique de contrats et vérifier que la valeur change. Si vous souhaitez appeler le contrat intelligent à partir d'une adresse différente, vous devrez changer l'adresse dans la sélection de compte de Remix. diff --git a/public/content/translations/fr/developers/tutorials/ipfs-decentralized-ui/index.md b/public/content/translations/fr/developers/tutorials/ipfs-decentralized-ui/index.md new file mode 100644 index 00000000000..a468c28e963 --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/ipfs-decentralized-ui/index.md @@ -0,0 +1,73 @@ +--- +title: "IPFS pour les interfaces utilisateur décentralisées" +description: "Ce tutoriel explique au lecteur comment utiliser l'IPFS pour stocker l'interface utilisateur d'une dapp. Bien que les données et la logique métier de l'application soient décentralisées, sans une interface utilisateur résistante à la censure, les utilisateurs pourraient quand même en perdre l'accès." +author: Ori Pomerantz +tags: [ "ipfs" ] +skill: beginner +lang: fr +published: 2024-06-29 +--- + +Vous avez écrit une nouvelle dapp incroyable. Vous avez même écrit une [interface utilisateur](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/) pour celle-ci. Mais maintenant, vous craignez que quelqu'un tente de la censurer en faisant tomber votre interface utilisateur, qui n'est qu'un simple serveur dans le cloud. Dans ce tutoriel, vous apprendrez comment éviter la censure en plaçant votre interface utilisateur sur le **[système de fichiers interplanétaire (IPFS)](https://ipfs.tech/developers/)** afin que toute personne intéressée puisse l'épingler sur un serveur pour un accès futur. + +Vous pourriez utiliser un service tiers tel que [Fleek](https://resources.fleek.xyz/docs/) pour faire tout le travail. Ce tutoriel s'adresse aux personnes qui veulent en faire assez pour comprendre ce qu'elles font, même si cela demande plus de travail. + +## Démarrer en local {#getting-started-locally} + +Il existe de multiples [fournisseurs IPFS tiers](https://docs.ipfs.tech/how-to/work-with-pinning-services/#use-a-third-party-pinning-service), mais il est préférable de commencer par exécuter IPFS localement pour les tests. + +1. Installez l'[interface utilisateur d'IPFS](https://docs.ipfs.tech/install/ipfs-desktop/#install-instructions). + +2. Créez un répertoire avec votre site web. Si vous utilisez [Vite](https://vite.dev/), utilisez cette commande : + + ```sh + pnpm vite build + ``` + +3. Dans IPFS Desktop, cliquez sur **Importer > Dossier** et sélectionnez le répertoire que vous avez créé à l'étape précédente. + +4. Sélectionnez le dossier que vous venez de télécharger et cliquez sur **Renommer**. Donnez-lui un nom plus significatif. + +5. Sélectionnez-le à nouveau et cliquez sur **Partager le lien**. Copiez l'URL dans le presse-papiers. Le lien sera similaire à `https://ipfs.io/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`. + +6. Cliquez sur **Statut**. Développez l'onglet **Avancé** pour voir l'adresse de la passerelle. Par exemple, sur mon système, l'adresse est `http://127.0.0.1:8080`. + +7. Combinez le chemin de l'étape du lien avec l'adresse de la passerelle pour trouver votre adresse. Par exemple, pour l'exemple ci-dessus, l'URL est `http://127.0.0.1:8080/ipfs/QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`. Ouvrez cette URL dans un navigateur pour voir votre site. + +## Mise en ligne {#uploading} + +Vous pouvez donc maintenant utiliser IPFS pour servir des fichiers localement, ce qui n'est pas très excitant. L'étape suivante consiste à les rendre disponibles au monde entier lorsque vous êtes hors ligne. + +Il existe un certain nombre de [services d'épinglage](https://docs.ipfs.tech/concepts/persistence/#pinning-services) bien connus. Choisissez l'un d'entre eux. Quel que soit le service que vous utilisez, vous devez créer un compte et lui fournir l'**identifiant de contenu (CID)** dans votre bureau IPFS. + +Personnellement, j'ai trouvé que [4EVERLAND](https://docs.4everland.org/storage/4ever-pin/guides) était le plus facile à utiliser. Voici les instructions pour cela : + +1. Accédez au [tableau de bord](https://dashboard.4everland.org/overview) et connectez-vous avec votre portefeuille. + +2. Dans la barre latérale gauche, cliquez sur **Stockage > 4EVER Pin**. + +3. Cliquez sur **Télécharger > CID sélectionné**. Donnez un nom à votre contenu et fournissez le CID depuis le bureau IPFS. Actuellement, un CID est une chaîne de caractères qui commence par `Qm` suivi de 44 lettres et chiffres qui représentent un hachage [encodé en base 58](https://medium.com/bootdotdev/base64-vs-base58-encoding-c25553ff4524), tel que `QmaCuQ7yN6iyBjLmLGe8YiFuCwnePoKfVu6ue8vLBsLJQJ`, mais [cela est susceptible de changer](https://docs.ipfs.tech/concepts/content-addressing/#version-1-v1). + +4. Le statut initial est **En file d'attente**. Rechargez jusqu'à ce qu'il passe à **Épinglé**. + +5. Cliquez sur votre CID pour obtenir le lien. Vous pouvez voir mon application [ici](https://bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im.ipfs.dweb.link/). + +6. Vous devrez peut-être activer votre compte pour qu'il soit épinglé pendant plus d'un mois. L'activation du compte coûte environ 1 $. Si vous l'avez fermé, déconnectez-vous et reconnectez-vous pour qu'on vous demande de l'activer à nouveau. + +## Utilisation depuis IPFS {#using-from-ipfs} + +À ce stade, vous disposez d'un lien vers une passerelle centralisée qui sert votre contenu IPFS. En bref, votre interface utilisateur est peut-être un peu plus sûre, mais elle n'est toujours pas résistante à la censure. Pour une véritable résistance à la censure, les utilisateurs doivent utiliser IPFS [directement depuis un navigateur](https://docs.ipfs.tech/install/ipfs-companion/#prerequisites). + +Une fois que vous l'avez installé (et que le bureau IPFS fonctionne), vous pouvez aller sur [/ipfs/``](https://any.site/ipfs/bafybeifqka2odrne5b6l5guthqvbxu4pujko2i6rx2zslvr3qxs6u5o7im) sur n'importe quel site et vous obtiendrez ce contenu, servi de manière décentralisée. + +## Inconvénients {#drawbacks} + +Vous ne pouvez pas supprimer de manière fiable les fichiers IPFS, donc tant que vous modifiez votre interface utilisateur, il est probablement préférable de la laisser centralisée, ou d'utiliser le [système de nom interplanétaire (IPNS)](https://docs.ipfs.tech/concepts/ipns/#mutability-in-ipfs), un système qui fournit la mutabilité au-dessus d'IPFS. Bien sûr, tout ce qui est mutable peut être censuré, dans le cas d'IPNS en faisant pression sur la personne possédant la clé privée à laquelle il correspond. + +De plus, certains paquets ont un problème avec IPFS, donc si votre site web est très compliqué, ce n'est peut-être pas une bonne solution. Et bien sûr, tout ce qui repose sur l'intégration d'un serveur ne peut pas être décentralisé simplement en ayant le côté client sur IPFS. + +## Conclusion {#conclusion} + +Tout comme Ethereum vous permet de décentraliser la base de données et la logique métier de votre dapp, IPFS vous permet de décentraliser l'interface utilisateur. Cela vous permet de fermer un autre vecteur d'attaque contre votre dapp. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md b/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md index 8025a28644f..96291c74fb5 100644 --- a/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md +++ b/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-with-create-eth-app/index.md @@ -1,14 +1,15 @@ --- -title: Démarrer le développement de votre interface dApp avec create-eth-app -description: Aperçu de l'utilisation de create-eth-app et de ses fonctionnalités +title: "Démarrez le développement de votre interface de dApp avec create-eth-app." +description: "Un aperçu de l'utilisation de create-eth-app et de ses fonctionnalités." author: "Markus Waas" tags: - - "create-eth-app" - - "frontend" - - "javascript" - - "ethers.js" - - "the graph" - - "DeFi" + [ + "frontend", + "javascript", + "ethers.js", + "the graph", + "DeFi" + ] skill: beginner lang: fr published: 2020-04-27 @@ -16,11 +17,11 @@ source: soliditydeveloper.com sourceUrl: https://soliditydeveloper.com/create-eth-app --- -La dernière fois nous nous sommes intéressés à [Solidity](https://soliditydeveloper.com/solidity-overview-2020) et avons mentionné [create-eth-app](https://github.com/PaulRBerg/create-eth-app). Vous allez maintenant découvrir comment l'utiliser, quelles fonctionnalités y sont intégrées et comment l'étendre encore. Initiée par Paul Razvan Berg, fondateur de [Sablier](http://sablier.com/), cette application livrée avec plusieurs intégrations facultatives au choix va vous permettre de débuter le développement de votre interface. +La dernière fois, nous avons examiné [la vue d'ensemble de Solidity](https://soliditydeveloper.com/solidity-overview-2020) et déjà mentionné [create-eth-app](https://github.com/PaulRBerg/create-eth-app). Vous allez maintenant découvrir comment l'utiliser, quelles fonctionnalités sont intégrées et des idées supplémentaires sur la façon de l'étendre. Lancée par Paul Razvan Berg, le fondateur de [Sablier](http://sablier.com/), cette application donnera un coup d'envoi à votre développement d'interface et est livrée avec plusieurs intégrations optionnelles au choix. ## Installation {#installation} -L'installation nécessite au minimum Yarn 0.25 (`npm install yarn --global`). L'installation est aussi simple que l'exécution : +L'installation nécessite Yarn 0.25 ou une version ultérieure (`npm install yarn --global`). C'est aussi simple que d'exécuter : ```bash yarn create eth-app my-eth-app @@ -28,44 +29,44 @@ cd my-eth-app yarn react-app:start ``` -Elle s'appuie sur [create-react-app](https://github.com/facebook/create-react-app). Pour voir votre application, ouvrez `http://localhost:3000/`. Lorsque vous êtes prêt à déployer en production, créez un paquet minifié avec le constructeur yarn. Un moyen simple de l'héberger est [Netlify](https://www.netlify.com/). Vous pouvez créer un dépôt GitHub, l'ajouter à Netlify, configurer la commande de construction et le tour est joué ! Votre application sera hébergée et utilisable par tout le monde. Et tout ceci gratuitement. +Elle utilise [create-react-app](https://github.com/facebook/create-react-app) sous le capot. Pour voir votre application, ouvrez `http://localhost:3000/`. Quand vous êtes prêt à déployer en production, créez un paquet minifié avec `yarn build`. Une façon simple de l'héberger serait d'utiliser [Netlify](https://www.netlify.com/). Vous pouvez créer un dépôt GitHub, l'ajouter à Netlify, configurer la commande de construction et le tour est joué ! Votre application sera hébergée et utilisable par tout le monde. Et tout cela, gratuitement. ## Fonctionnalités {#features} -### React & create-react-app {#react--create-react-app} +### React et create-react-app {#react--create-react-app} -Premièrement, le coeur de l'application : React et toutes les fonctionnalités additionnelles livrées avec _create-react-app_. Utiliser cette seule application est une excellente option si vous ne souhaitez pas intégrer Ethereum. [React](https://reactjs.org/) rend la construction d'interfaces utilisateur interactives très facile. La prise en main n'est peut-être pas aussi facile qu'avec [Vue](https://vuejs.org/), mais l'application est encore largement utilisée, possède plus de fonctionnalités et surtout offre un choix de plusieurs milliers de bibliothèques supplémentaires. Avec _create-react-app_, le démarrage est très simple. L'application inclut : +Tout d'abord, le cœur de l'application : React et toutes les fonctionnalités supplémentaires fournies avec _create-react-app_. Utiliser cette seule application est une excellente option si vous ne souhaitez pas intégrer Ethereum. [React](https://react.dev/) en soi facilite grandement la création d'interfaces utilisateur interactives. Sa prise en main n'est peut-être pas aussi facile qu'avec [Vue](https://vuejs.org/), mais il reste le plus utilisé, il possède plus de fonctionnalités et, surtout, il offre un choix de milliers de bibliothèques supplémentaires. _create-react-app_ facilite également le démarrage et inclut : -- React, JSX, ES6, TypeScript et le support pour Flow syntax. -- Langages complémentaires à ES6 comme l'opérateur de propagation d'objet. -- CSS auto-préfixé, pour se passer de -webkit- ou d'autres préfixes. +- Prise en charge de la syntaxe React, JSX, ES6, TypeScript et Flow. +- Des fonctionnalités de langage au-delà d'ES6, comme l'opérateur de décomposition d'objet. +- CSS avec préfixes automatiques, pour que vous n'ayez pas besoin de `-webkit-` ou d'autres préfixes. - Un exécuteur de test unitaire interactif rapide avec une prise en charge intégrée pour les rapports de couverture. -- Un serveur de développement en direct qui signale les erreurs courantes. -- Un script de construction pour associer du JS, du CSS et des images en vue de la mise en production, avec des hachages et une cartographie du code source. +- Un serveur de développement en direct qui avertit des erreurs courantes. +- Un script de construction pour empaqueter le JS, le CSS et les images pour la production, avec des hachages et des sourcemaps. -_create-react-app_, en particulier, fait usage des nouveaux [effets hooks](https://reactjs.org/docs/hooks-effect.html). Une méthode pour écrire de puissants mais très petits composants, dits fonctionnels. Voir ci-dessous la section sur Apollo pour savoir comment ils sont utilisés dans _create-react-app_. +L'application _create-eth-app_ utilise en particulier les nouveaux [effets de hooks](https://legacy.reactjs.org/docs/hooks-effect.html). Une méthode pour écrire de puissants mais très petits composants, dits fonctionnels. Consultez la section ci-dessous sur Apollo pour voir comment ils sont utilisés dans _create-eth-app_. -### Espaces de travail Yarn {#yarn-workspaces} +### Yarn Workspaces {#yarn-workspaces} -[Les espaces de travail Yarn](https://classic.yarnpkg.com/en/docs/workspaces/) vous permettent de disposer de plusieurs paquets, mais également d'être en mesure de tous les gérer à partir du dossier racine et d'installer toutes leurs dépendances en une fois en utilisant `yarn install`. Ceci est particulièrement adapté pour les petits packs additionnels, tels que les adresses de contrats intelligents/la gestion ABI (les informations sur l'endroit où vous avez déployé tels contrats intelligents et comment communiquer avec eux) ou l'intégration de graphes, les deux parties de `create-eth-app`. +[Yarn Workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) vous permettent d'avoir plusieurs paquets, tout en pouvant les gérer tous depuis le dossier racine et installer les dépendances pour tous en même temps en utilisant `yarn install`. C'est particulièrement pertinent pour les paquets supplémentaires plus petits, comme la gestion des adresses de contrats intelligents/ABI (les informations sur l'endroit où vous avez déployé quels contrats intelligents et comment communiquer avec eux) ou l'intégration de graphe, qui font tous deux partie de `create-eth-app`. ### ethers.js {#ethersjs} -Si [Web3](https://docs.web3js.org/) est encore largement utilisé, [ethers.js](https://docs.ethers.io/) a davantage été employé comme alternative l'année dernière et est intégré à _create-eth-app_. Vous pouvez travailler avec celui-ci, le faire évoluer vers Web3 ou envisager une mise à niveau pour passer à [ethers.js v5](https://docs.ethers.org/v5/) qui n'est pratiquement plus en version bêta. +Bien que [Web3](https://docs.web3js.org/) soit encore majoritairement utilisé, [ethers.js](https://docs.ethers.io/) a gagné en popularité comme alternative au cours de l'année dernière et c'est lui qui est intégré dans _create-eth-app_. Vous pouvez travailler avec celui-ci, le remplacer par Web3 ou envisager de passer à [ethers.js v5](https://docs.ethers.org/v5/) qui est presque sorti de la version bêta. -### Le réseau Graph {#the-graph} +### The Graph {#the-graph} -[GraphQL](https://graphql.org/) est un moyen alternatif de gérer les données par rapport à une [API Restful](https://restfulapi.net/). Il offre plusieurs avantages par rapport aux APIs REST, en particulier pour les données décentralisées de la blockchain. Si vous êtes intéressé par le raisonnement qui le sous-tend, jetez un œil à [GraphQL va propulser le Web décentralisé](https://medium.com/graphprotocol/graphql-will-power-the-decentralized-web-d7443a69c69a). +[GraphQL](https://graphql.org/) est une méthode alternative de gestion des données par rapport à une [API RESTful](https://restfulapi.net/). Il présente plusieurs avantages par rapport aux API RESTful, en particulier pour les données de blockchain décentralisées. Si les raisons qui motivent ce choix vous intéressent, consultez [GraphQL Will Power the Decentralized Web](https://medium.com/graphprotocol/graphql-will-power-the-decentralized-web-d7443a69c69a). -Vous récupérez normalement directement les données de votre contrat intelligent. Vous souhaitez connaître l'instant précis de la dernière transaction ? Appelez simplement `MyContract.methods.latestTradeTime().call()` qui récupère les données d'un nœud Ethereum comme Infura dans votre dApp. Mais que faire si vous avez besoin de centaines de points de données différents ? Il en résulterait des centaines d'extractions de données vers le nœud, nécessitant à chaque fois un [RTT](https://wikipedia.org/wiki/Round-trip_delay_time) qui ralentirait votre dApp et lui ferait perdre son efficacité. Pour éviter cela, une solution pourrait être d'utiliser une fonction d'appel de récupération dans votre contrat qui restitue plusieurs données à la fois. Ce n'est cependant pas toujours idéal. +Habituellement, vous récupérez les données directement depuis votre contrat intelligent. Vous voulez lire l'heure de la dernière transaction ? Il suffit d'appeler `MyContract.methods.latestTradeTime().call()` qui récupère les données d'un nœud Ethereum dans votre dapp. Mais que faire si vous avez besoin de centaines de points de données différents ? Cela entraînerait des centaines de récupérations de données vers le nœud, chacune nécessitant un [RTT](https://wikipedia.org/wiki/Round-trip_delay_time), ce qui rendrait votre dapp lente et inefficace. Une solution de contournement pourrait être une fonction d'appel de récupération dans votre contrat qui renvoie plusieurs données à la fois. Ce n'est cependant pas toujours idéal. -Vous pourriez également être intéressé par les données historiques. Vous souhaitez peut-être connaître non seulement le moment de la dernière transaction mais également le moment de chacune des transactions que vous avez réalisées vous-même. Utilisez le paquet subgraph de _create-eth-app_, lisez la [documentation](https://thegraph.com/docs/en/subgraphs/developing/creating/starting-your-subgraph) et adaptez-la à vos propres contrats. Si vous êtes à la recherche de contrats intelligents populaires, il se peut même qu'il en existe déjà un avec subgraph. Jetez un œil à [l'explorateur de sous-graphes](https://thegraph.com/explorer/). +Vous pourriez également être intéressé par les données historiques. Vous souhaitez peut-être connaître non seulement le moment de la dernière transaction mais également le moment de chacune des transactions que vous avez réalisées vous-même. Utilisez le paquet subgraph de _create-eth-app_, lisez la [documentation](https://thegraph.com/docs/en/subgraphs/developing/creating/starting-your-subgraph) et adaptez-le à vos propres contrats. Si vous recherchez des contrats intelligents populaires, il se peut même qu'un subgraph existe déjà. Consultez l'[explorateur de subgraphs](https://thegraph.com/explorer/). -Une fois que vous disposez d'un subgraph, vous pouvez écrire une simple requête dans votre dApp afin de récupérer toutes les données importantes de la blockchain, y compris les données historiques dont vous avez besoin. Une seule demande de récupération suffit. +Une fois que vous disposez d'un subgraph, vous pouvez écrire une simple requête dans votre dapp afin de récupérer toutes les données importantes de la blockchain, y compris les données historiques dont vous avez besoin. Une seule récupération suffit. ### Apollo {#apollo} -Grâce à l'intégration d'[Apollo Boost](https://www.apollographql.com/docs/react/get-started/), vous pouvez facilement intégrer Graph dans votre dApp React. Surtout lorsque vous utilisez [des hooks React et Apollo](https://www.apollographql.com/blog/apollo-client-now-with-react-hooks), récupérer des données est aussi simple que d'écrire une requête GraphQl dans votre composant: +Grâce à l'intégration de [Apollo Boost](https://www.apollographql.com/docs/react/get-started/), vous pouvez facilement intégrer The Graph dans votre dapp React. En particulier lorsque vous utilisez les [hooks React et Apollo](https://www.apollographql.com/blog/apollo-client-now-with-react-hooks), la récupération de données est aussi simple que d'écrire une seule requête GraphQL dans votre composant : ```js const { loading, error, data } = useQuery(myGraphQlQuery) @@ -77,34 +78,34 @@ React.useEffect(() => { }, [loading, error, data]) ``` -## Modèles (Templates) {#templates} +## Modèles {#templates} -En haut, il est possible de choisir parmi différents modèles. À ce jour, vous pouvez utiliser une intégration Aave, Compound, UniSwap ou Sablier. Ces modèles ajoutent tous des adresses importantes de contrats intelligents de service ainsi que des intégrations pré-construites de subgraph. Il suffit d'ajouter le modèle à la commande de création comme `yarn create eth-app my-eth-app --with-template aav`. +En plus, vous pouvez choisir parmi plusieurs modèles différents. Pour l'instant, vous pouvez utiliser une intégration Aave, Compound, Uniswap ou Sablier. Ces modèles ajoutent tous des adresses importantes de contrats intelligents de service ainsi que des intégrations pré-construites de subgraph. Il suffit d'ajouter le modèle à la commande de création, comme `yarn create eth-app my-eth-app --with-template aave`. ### Aave {#aave} -[Aave](https://aave.com/) est un marché décentralisé de prêt d'argent. Les déposants fournissent des liquidités au marché pour gagner un revenu passif, tandis que les emprunteurs peuvent emprunter avec des garanties. Une fonctionnalité exclusive d'Aave réside dans ces [prêts flash](https://docs.aave.com/developers/guides/flash-loans) qui vous permettent d'emprunter de l'argent sans aucune garantie, pour autant que vous remboursiez le prêt en une seule transaction. Cela peut être utile par exemple pour vous donner de l'argent supplémentaire sur l'arbitrage d'échange. +[Aave](https://aave.com/) est un marché de prêt d'argent décentralisé. Les déposants fournissent des liquidités au marché pour gagner un revenu passif, tandis que les emprunteurs peuvent emprunter avec des garanties. Une caractéristique unique d'Aave sont les [prêts flash](https://aave.com/docs/developers/flash-loans) qui vous permettent d'emprunter de l'argent sans aucune garantie, tant que vous remboursez le prêt en une seule transaction. Cela peut être utile, par exemple, pour vous donner des liquidités supplémentaires pour du trading d'arbitrage. Les jetons échangés qui vous rapportent des intérêts sont appelés _aTokens_. -Si vous choisissez d'intégrer Aave avec _create-eth-app_, vous obtiendrez une [intégration subgraph](https://docs.aave.com/developers/getting-started/using-graphql). Aave utilise The Graph et vous fournit déjà plusieurs Subgraphs prêts à l'emploi sur [Ropsten](https://thegraph.com/explorer/subgraph/aave/protocol-ropsten) et [le réseau principal](https://thegraph.com/explorer/subgraph/aave/protocol) en formulaire [brut](https://thegraph.com/explorer/subgraph/aave/protocol-raw) ou [formaté](https://thegraph.com/explorer/subgraph/aave/protocol). +Lorsque vous choisissez d'intégrer Aave avec _create-eth-app_, vous obtiendrez une [intégration de subgraph](https://docs.aave.com/developers/getting-started/using-graphql). Aave utilise The Graph et vous fournit déjà plusieurs subgraphs prêts à l'emploi sur [Ropsten](https://thegraph.com/explorer/subgraph/aave/protocol-ropsten) et [Mainnet](https://thegraph.com/explorer/subgraph/aave/protocol) sous forme [brute](https://thegraph.com/explorer/subgraph/aave/protocol-raw) ou [formatée](https://thegraph.com/explorer/subgraph/aave/protocol). -![Aave Flash Loan meme - "Ouah, si je pouvais garder mon prêt flash plus longtemps qu'une transaction, ce serait génial" ;](./flashloan-meme.png) +![Mème sur les prêts flash d'Aave – « Ouais, si je pouvais garder mon prêt flash plus d'une transaction, ce serait génial »](./flashloan-meme.png) ### Compound {#compound} -[Compound](https://compound.finance/) est similaire à Aave. L'intégration inclut déjà le nouveau [Compound v2 Subgraph](https://medium.com/graphprotocol/https-medium-com-graphprotocol-compound-v2-subgraph-highlight-a5f38f094195). Les intérêts gagnés des jetons sont ici étonnamment appelés _cTokens_. +[Compound](https://compound.finance/) est similaire à Aave. L'intégration inclut déjà le nouveau [subgraph Compound v2](https://medium.com/graphprotocol/https-medium-com-graphprotocol-compound-v2-subgraph-highlight-a5f38f094595). De manière surprenante, les jetons qui rapportent des intérêts sont ici appelés _cTokens_. ### Uniswap {#uniswap} -[Uniswap](https://uniswap.exchange/) est un système d'échange décentralisé (DEX). Les fournisseurs de liquidités peuvent percevoir des commissions en fournissant les jetons ou l'éther requis pour les deux parties d'une transaction. Le protocole est largement utilisé et dispose donc de liquidités très nombreuses pour une très large gamme de jetons. Vous pouvez facilement l'intégrer dans votre dApp pour permettre, par exemple, aux utilisateurs d'échanger leur ETH contre du DAI. +[Uniswap](https://uniswap.exchange/) est une plateforme d'échange décentralisée (DEX). Les fournisseurs de liquidités peuvent percevoir des commissions en fournissant les jetons ou l'éther requis pour les deux parties d'une transaction. Il est largement utilisé et dispose donc de l'une des plus grandes liquidités pour une très large gamme de jetons. Vous pouvez facilement l'intégrer dans votre dapp pour, par exemple, permettre aux utilisateurs d'échanger leurs ETH contre des DAI. -Malheureusement, à l'heure où ces lignes sont écrites, l'intégration est uniquement proposée pour Uniswap v1 et non pour la toute nouvelle version [v2](https://uniswap.org/blog/uniswap-v2/). +Malheureusement, au moment de la rédaction, l'intégration n'est disponible que pour Uniswap v1 et non pour la [v2 qui vient de sortir](https://uniswap.org/blog/uniswap-v2/). ### Sablier {#sablier} -[Sablier](https://sablier.com/) permet aux utilisateurs d'effectuer des paiements en continu. Au lieu d'un seul versement, vous recevez en fait votre argent en continu sans avoir rien d'autre à faire après la mise en place initiale. L'intégration inclut son [propre sous-graphe](https://thegraph.com/explorer/subgraph/sablierhq/sablier). +[Sablier](https://sablier.com/) permet aux utilisateurs d'effectuer des paiements d'argent en continu. Au lieu d'un seul versement, vous recevez en fait votre argent en continu sans avoir rien d'autre à faire après la mise en place initiale. L'intégration inclut son [propre subgraph](https://thegraph.com/explorer/subgraph/sablierhq/sablier). ## Et après ? {#whats-next} -Si vous avez des questions sur _create-eth-app_, allez sur le [serveur de la Communauté Sablier](https://discord.gg/bsS8T47), où vous pouvez entrer en contact avec les auteurs de _create-eth-app_. Dans un premier temps, vous pourriez vouloir intégrer un framework d'interface utilisateur comme [Material UI](https://material-ui.com/), écrire des requêtes GraphQL pour les données dont vous avez réellement besoin et configurer le déploiement. +Si vous avez des questions sur _create-eth-app_, rendez-vous sur le [serveur communautaire de Sablier](https://discord.gg/bsS8T47), où vous pourrez contacter les auteurs de _create-eth-app_. Comme premières étapes, vous pourriez vouloir intégrer un framework d'interface utilisateur comme [Material UI](https://mui.com/material-ui/), écrire des requêtes GraphQL pour les données dont vous avez réellement besoin et configurer le déploiement. diff --git a/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md b/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md index 38b110610f2..2b534b86cb5 100644 --- a/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md +++ b/public/content/translations/fr/developers/tutorials/kickstart-your-dapp-frontend-development-wth-create-eth-app/index.md @@ -1,6 +1,6 @@ --- -title: Démarrer le développement de votre interface dApp avec create-eth-app -description: Aperçu de l'utilisation de create-eth-app et de ses fonctionnalités +title: "Démarrer le développement de votre interface dApp avec create-eth-app" +description: "Aperçu de l'utilisation de create-eth-app et de ses fonctionnalités" author: "Markus Waas" tags: - "create-eth-app" diff --git a/public/content/translations/fr/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md b/public/content/translations/fr/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md index e7cad8f1608..0518d560e8a 100644 --- a/public/content/translations/fr/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md +++ b/public/content/translations/fr/developers/tutorials/learn-foundational-ethereum-topics-with-sql/index.md @@ -1,11 +1,8 @@ --- title: Apprendre les sujets fondamentaux d'Ethereum avec SQL -description: Ce tutoriel a pour objectif d'aider les lecteurs à comprendre les concepts fondamentaux d'Ethereum, y compris les transactions, les blocs et le gaz en interrogeant les données sur chaîne avec le Langage de Requête Structurée (SQL). +description: "Ce tutoriel aide les lecteurs à comprendre les concepts fondamentaux d'Ethereum, notamment les transactions, les blocs et le gaz, en interrogeant les données sur la chaîne avec le langage de requête structuré (SQL)." author: "Paul Apivat" -tags: - - "SQL" - - "Requêtes" - - "Transactions" +tags: [ "SQL", "Requêtes", "Transactions" ] skill: beginner lang: fr published: 2021-05-11 @@ -13,27 +10,27 @@ source: paulapivat.com sourceUrl: https://paulapivat.com/post/query_ethereum/ --- -De nombreux tutoriels Ethereum ciblent les développeurs, mais il existe un manque de ressources éducatives pour les analystes de données ou pour les personnes qui souhaitent voir des données en chaîne sans faire tourner un client ou un nœud. +De nombreux tutoriels Ethereum s'adressent aux développeurs, mais il y a un manque de ressources éducatives pour les analystes de données ou pour les personnes qui souhaitent consulter les données sur la chaîne sans exécuter un client ou un nœud. -Ce tutoriel a pour objectif d'aider les lecteurs à comprendre les concepts fondamentaux d'Ethereum, y compris les transactions, les blocs et la notion de gaz en interrogeant les données en chaîne avec un langage SQL via une interface fournie par [Dune Analytics](https://dune.xyz/home). +Ce tutoriel aide les lecteurs à comprendre les concepts fondamentaux d'Ethereum, notamment les transactions, les blocs et le gaz, en interrogeant les données sur la chaîne avec le langage de requête structuré (SQL) via une interface fournie par [Dune Analytics](https://dune.com/). -Les données en chaîne (On-chain) peuvent nous aider à comprendre Ethereum, le réseau, permettre des économies de puissance informatique et devrait servir de base à la compréhension des défis auxquels Ethereum est confronté aujourd'hui (par exemple : la hausse des prix du gaz) et, plus important encore, avoir des discussions sur les solutions évolutives. +Les données sur la chaîne peuvent nous aider à comprendre Ethereum, le réseau, et son rôle en tant qu'économie pour la puissance de calcul. Elles devraient servir de base à la compréhension des défis auxquels Ethereum est confronté aujourd'hui (par exemple, la hausse des prix du gaz) et, plus important encore, aux discussions sur les solutions de mise à l'échelle. ### Transactions {#transactions} -Le voyage d'un utilisateur sur Ethereum débute par l'initialisation d'un compte utilisateur contrôlé ou d'une entité avec un solde ETH. Il existe deux types de comptes - contrôlé par l'utilisateur ou un contrat intelligent (voir [ethereum.org](/developers/docs/accounts/)). +Le parcours d'un utilisateur sur Ethereum commence par l'initialisation d'un compte contrôlé par l'utilisateur ou d'une entité disposant d'un solde en ETH. Il existe deux types de comptes : les comptes contrôlés par l'utilisateur ou les contrats intelligents (voir [ethereum.org](/developers/docs/accounts/)). -N'importe quel compte peut être consulté sur un explorateur de blocs comme [Etherscan](https://etherscan.io/). Les explorateurs de blocs sont votre portail vers les données Ethereum. Ils affichent, en temps réel, des données sur les blocs, les transactions, les mineurs, les comptes et autres activités en chaîne (voir [ici](/developers/docs/data-and-analytics/block-explorers/)). +N'importe quel compte peut être consulté sur un explorateur de blocs comme [Etherscan](https://etherscan.io/) ou [Blockscout](https://eth.blockscout.com/). Les explorateurs de blocs sont un portail vers les données d'Ethereum. Ils affichent, en temps réel, les données sur les blocs, les transactions, les mineurs, les comptes et autres activités sur la chaîne (voir [ici](/developers/docs/data-and-analytics/block-explorers/)). -Cependant, un utilisateur peut vouloir interroger directement les données pour reconsidérer les informations fournies par les explorateurs de blocs externes. [Dune Analytics](https://duneanalytics.com/) fournit cette capacité à quiconque ayant une certaine connaissance de SQL. +Cependant, un utilisateur peut souhaiter interroger directement les données pour les rapprocher des informations fournies par des explorateurs de blocs externes. [Dune Analytics](https://dune.com/) offre cette possibilité à toute personne ayant quelques connaissances en SQL. -Pour référence, le compte du contrat intelligent de la Fondation Ethereum (EF) peut être consulté sur [Etherscan](https://etherscan.io/address/0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae). +À titre de référence, le compte de contrat intelligent de l'Ethereum Foundation (EF) peut être consulté sur [Blockscout](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe). -Une chose à noter est que tous les comptes, y compris ceux de la Fondation Ethereum, disposent d'une adresse publique qui peut être utilisée pour envoyer et recevoir des transactions. +Il est à noter que tous les comptes, y compris celui de l'EF, possèdent une adresse publique qui peut être utilisée pour envoyer et recevoir des transactions. -Le solde du compte Etherscan comprend des transactions régulières et des transactions internes. Les transactions internes, malgré le nom, ne sont pas des transactions _réelles_ qui modifient l'état de la chaîne. Ce sont des transferts de valeur initiés par l'exécution d'un contrat ([source](https://ethereum.stackexchange.com/questions/3417/how-to-get-contract-internal-transactions)). Étant donné que les transactions internes n'ont pas de signature, elles ne sont **PAS** incluses sur la blockchain et ne peuvent pas être interrogées avec Dune Analytics. +Le solde du compte sur Etherscan comprend des transactions ordinaires et des transactions internes. Les transactions internes, malgré leur nom, ne sont pas des transactions _réelles_ qui modifient l'état de la chaîne. Ce sont des transferts de valeur initiés par l'exécution d'un contrat ([source](https://ethereum.stackexchange.com/questions/3417/how-to-get-contract-internal-transactions)). Puisque les transactions internes n'ont pas de signature, elles ne sont **pas** incluses sur la blockchain et ne peuvent pas être interrogées avec Dune Analytics. -Ainsi, ce tutoriel se concentrera sur les transactions dites régulières. Elles peuvent être questionnées ainsi : +Par conséquent, ce tutoriel se concentrera sur les transactions ordinaires. Elles peuvent être interrogées comme suit : ```sql WITH temp_table AS ( @@ -61,33 +58,33 @@ SELECT FROM temp_table ``` -Cela donnera les mêmes informations que celles fournies sur la page de transaction Etherscan. À titre de comparaison, voici les deux sources : +Cela produira les mêmes informations que celles fournies sur la page des transactions d'Etherscan. À titre de comparaison, voici les deux sources : #### Etherscan {#etherscan} ![](./etherscan_view.png) -[La page du contrat de la Fondation Ethereum sur Etherscan.](https://etherscan.io/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) +[Page du contrat de l'EF sur Blockscout.](https://eth.blockscout.com/address/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe) #### Dune Analytics {#dune-analytics} ![](./dune_view.png) -Vous pouvez trouver le tableau de bord [ici](https://duneanalytics.com/paulapivat/Learn-Ethereum). Cliquez sur la table pour voir la requête (voir aussi ci-dessus). +Vous pouvez trouver le tableau de bord [ici](https://dune.com/paulapivat/Learn-Ethereum). Cliquez sur le tableau pour voir la requête (également visible ci-dessus). -### Décortiquer les transactions {#breaking_down_transactions} +### Analyse détaillée des transactions {#breaking_down_transactions} -Une transaction soumise comprend plusieurs informations dont ([source](/developers/docs/transactions/) ) : +Une transaction soumise comprend plusieurs informations, notamment ([source](/developers/docs/transactions/)) : -- **Recipient** : (destinataire) l'adresse de réception (requête « to ») -- **Signature** : Alors que la clé privée d'un expéditeur signe une transaction, ce que nous pouvons demander avec SQL est l'adresse publique de l'expéditeur (« from »). -- **Value** : (valeur) Il s'agit du montant d'ETH transféré (voir colonne `ether`). -- **Data** : (données) il s'agit de données arbitraires qui ont été hachées (voir colonne `data`). -- **gasLimit** : Quantité maximum d’unités de gaz pouvant être consommée par la transaction. Les unités de gaz représentent les étapes de calcul. -- **maxPriorityFeePerGas** : la quantité maximale de gaz à inclure comme un pourboire pour le mineur. -- **maxFeePerGas** - le montant maximum de gaz prêt à être payé pour la transaction (incluant baseFeePerGas et maxPriorityFeePerGas) +- **Destinataire** : l'adresse de réception (interrogée en tant que "to") +- **Signature** : bien que la clé privée d'un expéditeur signe une transaction, ce que nous pouvons interroger avec SQL est l'adresse publique d'un expéditeur ("from"). +- **Valeur** : il s'agit du montant d'ETH transféré (voir la colonne `ether`). +- **Données** : il s'agit de données arbitraires qui ont été hachées (voir la colonne `data`) +- **gasLimit** – la quantité maximale d'unités de gaz qui peut être consommée par la transaction. Les unités de gaz représentent des étapes de calcul +- **maxPriorityFeePerGas** - le montant maximum de gaz à inclure comme pourboire pour le mineur +- **maxFeePerGas** - le montant maximum de gaz que l'on est prêt à payer pour la transaction (y compris `baseFeePerGas` et `maxPriorityFeePerGas`) -Nous pouvons interroger ces informations spécifiques pour les transactions à l'adresse publique de la Fondation Ethereum : +Nous pouvons interroger ces informations spécifiques pour les transactions vers l'adresse publique de l'Ethereum Foundation : ```sql SELECT @@ -104,17 +101,17 @@ WHERE "to" = '\xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe' ORDER BY block_time DESC ``` -### Les blocs {#blocks} +### Blocs {#blocks} -Chaque transaction va changer l'état de la machine virtuelle Ethereum ([EVM](/developers/docs/evm/)) ([source](/developers/docs/transactions/)). Les transactions sont diffusées sur le réseau pour être vérifiées et incluses dans un bloc. Chaque transaction est associée à un numéro de bloc. Pour consulter les données, nous pourrions interroger un numéro de bloc spécifique : 12396854 (le bloc le plus récent parmi les transactions de la Fondation Ethereum à ce jour, 11/05/21). +Chaque transaction modifiera l'état de la machine virtuelle Ethereum ([EVM](/developers/docs/evm/)) ([source](/developers/docs/transactions/)). Les transactions sont diffusées sur le réseau pour être vérifiées et incluses dans un bloc. Chaque transaction est associée à un numéro de bloc. Pour voir les données, nous pourrions interroger un numéro de bloc spécifique : 12396854 (le bloc le plus récent parmi les transactions de l'Ethereum Foundation au moment de la rédaction de cet article, 11/5/21). -De plus, lorsque nous interrogeons les deux blocs suivants, nous pouvons constater que chaque bloc contient le hachage du bloc précédent (c.-à-d. parent hash), illustrant la façon dont la blockchain est formée. +De plus, lorsque nous interrogeons les deux blocs suivants, nous pouvons voir que chaque bloc contient le hachage du bloc précédent (c'est-à-dire le hachage parent), ce qui illustre la façon dont la blockchain est formée. -Chaque bloc contient une référence vers le bloc parent. Ceci est affiché ci-dessous entre les colonnes `hash` et `parent_hash` ([source](/developers/docs/blocks/) ) : +Chaque bloc contient une référence à son bloc parent. Ceci est illustré ci-dessous entre les colonnes `hash` et `parent_hash` ([source](/developers/docs/blocks/)) : ![parent_hash](./parent_hash.png) -Voici la [requête](https://duneanalytics.com/queries/44856/88292) sur Dune Analytics : +Voici la [requête](https://dune.com/queries/44856/88292) sur Dune Analytics : ```sql SELECT @@ -128,18 +125,18 @@ WHERE "number" = 12396854 OR "number" = 12396855 OR "number" = 12396856 LIMIT 10 ``` -Nous pouvons examiner un bloc en interrogeant le temps, le numéro de bloc, la difficulté, l'empreinte numérique, le hachage parent et le nonce. +Nous pouvons examiner un bloc en interrogeant l'heure, le numéro de bloc, la difficulté, le hachage, le hachage parent et le nonce. -La seule chose que cette requête ne couvre pas est _la liste de transactions_ qui nécessite une requête séparée ci-dessous et la _racine d'état_. Un nœud complet ou archivé stockera toutes les transactions et transitions d'état, permettant aux clients d'interroger l'état de la chaîne à tout moment. Parce que cela nécessite un grand espace de stockage, nous pouvons séparer les données de la chaîne des données d'état : +La seule chose que cette requête ne couvre pas est la _liste des transactions_, qui nécessite une requête distincte ci-dessous, et la _racine d'état_. Un nœud complet ou d'archivage stockera toutes les transactions et les transitions d'état, ce qui permettra aux clients d'interroger l'état de la chaîne à tout moment. Comme cela nécessite un grand espace de stockage, nous pouvons séparer les données de la chaîne des données d'état : -- Données de chaîne (liste des blocs, transactions) -- Données d'état (résultat de la transition d'état pour chaque transaction) +- Données de la chaîne (liste des blocs, transactions) +- Données d'état (résultat de la transition d'état de chaque transaction) -La racine d'état tombe dans cette dernière et sont des données _implicites_ (non stockées sur la chaîne), alors que les données en chaîne sont explicites et stockées sur la chaîne elle-même ([source](https://ethereum.stackexchange.com/questions/359/where-is-the-state-data-stored)). +La racine d'état entre dans cette dernière catégorie et correspond à des données _implicites_ (non stockées sur la chaîne), tandis que les données de la chaîne sont explicites et stockées sur la chaîne elle-même ([source](https://ethereum.stackexchange.com/questions/359/where-is-the-state-data-stored)). -Pour ce tutoriel, nous nous concentrerons sur les données en chaîne que l'on _peut_ interroger avec SQL via Dune Analytics. +Pour ce tutoriel, nous nous concentrerons sur les données sur la chaîne qui _peuvent_ être interrogées avec SQL via Dune Analytics. -Comme indiqué ci-dessus, chaque bloc contient une liste de transactions, nous pouvons les consulter en filtrant un bloc spécifique. Nous allons essayer le bloc le plus récent, 12396854 : +Comme indiqué ci-dessus, chaque bloc contient une liste de transactions. Nous pouvons l'interroger en filtrant sur un bloc spécifique. Essayons avec le bloc le plus récent, 12396854 : ```sql SELECT * FROM ethereum."transactions" @@ -147,13 +144,13 @@ WHERE block_number = 12396854 ORDER BY block_time DESC` ``` -Voici la sortie SQL sur Dune : +Voici le résultat SQL sur Dune : ![](./list_of_txn.png) -Cet unique bloc étant ajouté à la chaîne change l'état de la Machine Virtuelle Ethereum ([EVM](/developers/docs/evm/)). Des dizaines de fois, des centaines de transactions sont vérifiées en même temps. Dans ce cas précis, 222 transactions ont été incluses. +L'ajout de ce seul bloc à la chaîne modifie l'état de la machine virtuelle Ethereum ([EVM](/developers/docs/evm/)). Des dizaines, parfois des centaines de transactions sont vérifiées en une seule fois. Dans ce cas précis, 222 transactions ont été incluses. -Pour voir combien de transactions ont réellement réussi, nous ajoutons un autre filtre pour compter les transactions réussies : +Pour voir combien d'entre elles ont effectivement abouti, nous ajoutons un autre filtre pour compter les transactions réussies : ```sql WITH temp_table AS ( @@ -170,22 +167,22 @@ Pour le bloc 12396854, sur un total de 222 transactions, 204 ont été vérifié ![](./successful_txn.png) -Les requêtes de transactions se produisent des dizaines de fois par seconde, mais les blocs sont produits environ une fois toutes les 15 secondes ([source](/developers/docs/blocks/)). +Les demandes de transaction se produisent des dizaines de fois par seconde, mais les blocs sont validés environ une fois toutes les 15 secondes ([source](/developers/docs/blocks/)). -Pour voir qu'un bloc est produit environ toutes les 15 secondes, nous pourrions prendre le nombre de secondes dans un jour (86400) divisé par 15 pour obtenir une estimation moyenne de blocs par jour (~ 5760). +Pour voir qu'un bloc est produit environ toutes les 15 secondes, nous pourrions prendre le nombre de secondes dans une journée (86 400) et le diviser par 15 pour obtenir une estimation du nombre moyen de blocs par jour (~ 5 760). -Le graphique des blocs Ethereum produits par jour (en 2016) est : +Le graphique des blocs Ethereum produits par jour (de 2016 à aujourd'hui) est le suivant : ![](./daily_blocks.png) -Le nombre moyen de blocs produits quotidiennement au cours de cette période est de ~5 874 : +Le nombre moyen de blocs produits quotidiennement au cours de cette période est d'environ 5 874 : ![](./avg_daily_blocks.png) -Les requêtes sont : +Les requêtes sont : ```sql -# query to visualize number of blocks produced daily since 2016 +# requête pour visualiser le nombre de blocs produits quotidiennement depuis 2016 SELECT DATE_TRUNC('day', time) AS dt, @@ -194,7 +191,7 @@ FROM ethereum."blocks" GROUP BY dt OFFSET 1 -# average number of blocks produced per day +# nombre moyen de blocs produits par jour WITH temp_table AS ( SELECT @@ -209,13 +206,13 @@ SELECT FROM temp_table ``` -Le nombre moyen de blocs produits par jour depuis 2016 est légèrement supérieur à ce nombrede 5 874. Alternativement, diviser 86 400 secondes par 5 874 blocs en moyenne donne 14,7 secondes, soit environ un bloc toutes les 15 secondes. +Le nombre moyen de blocs produits par jour depuis 2016 est légèrement supérieur à ce chiffre, à 5 874. Alternativement, en divisant 86 400 secondes par 5 874 blocs en moyenne, on obtient 14,7 secondes, soit environ un bloc toutes les 15 secondes. ### Gaz {#gas} -Les blocs sont limités en taille. La taille maximale de bloc est dynamique et varie en fonction de la demande sur le réseau, entre 12 500 000 et 25 000 000 d'unités. Des limites sont requises pour éviter que des blocs de taille arbitraire puissent déformer des nœuds complets en termes d'espace disque et de vitesse requise ([source](/developers/docs/blocks/)). +La taille des blocs est limitée. La taille maximale d'un bloc est dynamique et varie en fonction de la demande du réseau entre 12 500 000 et 25 000 000 d'unités. Des limites sont nécessaires pour empêcher que des blocs de taille arbitrairement grande ne surchargent les nœuds complets en termes d'espace disque et de vitesse ([source](/developers/docs/blocks/)). -Une façon de conceptualiser la limite de gaz par bloc est de la considérer comme l'**approvisionnement** de l'espace disponible d'un bloc dans lequel réaliser les transactions par lots. La limite de gaz du bloc peut être consultée et visualisée de 2016 à nos jours : +Une façon de conceptualiser la limite de gaz par bloc est de la considérer comme l'**offre** d'espace de bloc disponible pour regrouper les transactions. La limite de gaz par bloc peut être interrogée et visualisée de 2016 à aujourd'hui : ![](./avg_gas_limit.png) @@ -228,7 +225,7 @@ GROUP BY dt OFFSET 1 ``` -Ensuite, il existe le gaz réellement utilisé quotidiennement pour payer les calculs effectués sur la chaîne Ethereum (par exemple en envoyant une transaction, en appelant un contrat intelligent, en frappant un NFT). Ceci est la **demande** pour l'espace disponible de bloc Ethereum : +Ensuite, il y a le gaz réellement utilisé quotidiennement pour payer les calculs effectués sur la chaîne Ethereum (par exemple, envoyer une transaction, appeler un contrat intelligent, frapper un NFT). C'est la **demande** pour l'espace de bloc Ethereum disponible : ![](./daily_gas_used.png) @@ -241,17 +238,17 @@ GROUP BY dt OFFSET 1 ``` -Nous pouvons également juxtaposer ces deux graphiques pour voir comment la ligne **demand and supply** se présente : +Nous pouvons également juxtaposer ces deux graphiques pour voir comment l'**offre et la demande** s'alignent : ![gas_demand_supply](./gas_demand_supply.png) -Par conséquent, nous pouvons comprendre les prix du gaz en fonction de la demande en blocs Ethereum, au regard de l'offre disponible. +Par conséquent, nous pouvons comprendre les prix du gaz comme une fonction de la demande d'espace de bloc sur Ethereum, compte tenu de l'offre disponible. -Enfin, nous pourrions vouloir interroger les prix quotidiens moyens du gaz sur la chaîne Ethereum. Cela entraînera un temps de requête particulièrement long. Nous filtrerons donc notre requête sur le montant moyen de gaz payé par la Fondation Ethereum. +Enfin, nous pourrions vouloir interroger les prix moyens quotidiens du gaz pour la chaîne Ethereum. Cependant, cela entraînerait un temps de requête particulièrement long, nous allons donc filtrer notre requête sur le montant moyen de gaz payé par transaction par l'Ethereum Foundation. ![](./ef_daily_gas.png) -Nous pouvons voir les prix de gaz payés au fil des années pour les transactions à l'adresse de la Fondation Ethereum. Voici la requête : +Nous pouvons voir les prix du gaz payés pour toutes les transactions effectuées vers l'adresse de l'Ethereum Foundation au fil des ans. Voici la requête : ```sql SELECT @@ -265,8 +262,8 @@ ORDER BY block_time DESC ### Résumé {#summary} -Avec ce tutoriel, nous comprenons les concepts fondateurs d'Ethereum et comment fonctionne la blockchain d'Ethereum en interrogeant et en se donnant une idée des données en chaîne. +Grâce à ce tutoriel, nous avons compris les concepts fondamentaux d'Ethereum et le fonctionnement de la blockchain Ethereum en interrogeant les données sur la chaîne et en nous familiarisant avec elles. -Le tableau de bord qui contient tout le code utilisé dans ce tutoriel peut être trouvé [ici](https://duneanalytics.com/paulapivat/Learn-Ethereum). +Le tableau de bord qui contient tout le code utilisé dans ce tutoriel peut être trouvé [ici](https://dune.com/paulapivat/Learn-Ethereum). -Pour une plus grande utilisation des données à des fins d'analyse de web3 [vous pouvez me retrouver sur Twitter](https://twitter.com/paulapivat). +Pour plus d'utilisation des données pour explorer le web3, [retrouvez-moi sur Twitter](https://twitter.com/paulapivat). diff --git a/public/content/translations/fr/developers/tutorials/logging-events-smart-contracts/index.md b/public/content/translations/fr/developers/tutorials/logging-events-smart-contracts/index.md index 6f8a89afd02..cede259138d 100644 --- a/public/content/translations/fr/developers/tutorials/logging-events-smart-contracts/index.md +++ b/public/content/translations/fr/developers/tutorials/logging-events-smart-contracts/index.md @@ -1,12 +1,14 @@ --- -title: Consigner les données des contrats intelligents avec des événements -description: Introduction aux événements de contrats intelligents et manière dont vous pouvez les utiliser pour enregistrer les données +title: "Consigner les données des contrats intelligents avec des événements" +description: "Une introduction aux événements de contrat intelligent et comment les utiliser pour consigner des données." author: "jdourlens" tags: - - "contrats intelligents" - - "remix" - - "solidity" - - "événements" + [ + "contrats intelligents", + "remix", + "solidité", + "événements" + ] skill: intermediate lang: fr published: 2020-04-03 @@ -15,19 +17,19 @@ sourceUrl: https://ethereumdev.io/logging-data-with-events/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -Dans Solidity, [les événements](/developers/docs/smart-contracts/anatomy/#events-and-logs) envoient des signaux que les contrats intelligents peuvent lancer. Les dApps, ou tout autre élément connecté à l'API Ethereum JSON-RPC, peuvent écouter ces événements et agir en conséquence. Un événement peut également être indexé de sorte que son historique soit consultable ultérieurement. +En Solidity, les [événements](/developers/docs/smart-contracts/anatomy/#events-and-logs) sont des signaux que les contrats intelligents peuvent émettre. Les dapps, ou tout ce qui est connecté à l'API JSON-RPC d'Ethereum, peuvent écouter ces événements et agir en conséquence. Un événement peut également être indexé de sorte que son historique soit consultable ultérieurement. -## Évènements {#events} +## Événements {#events} -L'événement le plus courant sur la blockchain Ethereum au moment de l'écriture de cet article est l'événement de 'Transfer' qui est émis par les jetons ERC20 lorsque quelqu'un transfère des jetons. +L'événement le plus courant sur la blockchain Ethereum au moment de la rédaction de cet article est l'événement Transfer, qui est émis par les jetons ERC20 lorsque quelqu'un transfère des jetons. ```solidity event Transfer(address indexed from, address indexed to, uint256 value); ``` -La signature de l'événement est déclarée à l'intérieur du code du contrat et peut être émise avec le mot clé 'emit'. Par exemple, l'évènement 'transfert' enregistre qui a initié le transfert (_à partir de_), à qui (_à_) et combien de jetons ont été transférés (_valeur_). +La signature de l'événement est déclarée dans le code du contrat et peut être émise avec le mot-clé « emit ». Par exemple, l'événement de transfert enregistre qui a envoyé le transfert (_from_), à qui (_to_) et combien de jetons ont été transférés (_value_). -Si nous revenons à notre contrat intelligent Counter et décidons de procéder à un enregistrement chaque fois que la valeur est modifiée. Comme il n’est pas destiné à être déployé, ce contrat sert de base à la construction d’un autre contrat en l'étendant : cela s’appelle un contrat abstrait. Dans le cas de notre exemple de compteur, cela ressemblerait à ceci : +Si nous revenons à notre contrat intelligent Counter et décidons de consigner chaque fois que la valeur est modifiée. Comme ce contrat n'est pas destiné à être déployé mais à servir de base à la construction d'un autre contrat en l'étendant : on l'appelle un contrat abstrait. Dans le cas de notre exemple de compteur, cela ressemblerait à ceci : ```solidity pragma solidity 0.5.17; @@ -36,16 +38,16 @@ contract Counter { event ValueChanged(uint oldValue, uint256 newValue); - // Private variable of type unsigned int to keep the number of counts + // Variable privée de type entier non signé pour conserver le nombre de comptages uint256 private count = 0; - // Function that increments our counter + // Fonction qui incrémente notre compteur function increment() public { count += 1; emit ValueChanged(count - 1, count); } - // Getter to get the count value + // Getter pour obtenir la valeur du compteur function getCount() public view returns (uint256) { return count; } @@ -53,14 +55,14 @@ contract Counter { } ``` -Notez que : +Notez que : -- **Ligne 5**: nous déclarons notre événement et ce qu'il contient, l'ancienne valeur et la nouvelle valeur. +- **Ligne 5** : nous déclarons notre événement et ce qu'il contient, l'ancienne valeur et la nouvelle valeur. -- **Ligne 13** : Lorsque nous incrémentons notre variable de nombre, nous émettons l'événement. +- **Ligne 13** : Lorsque nous incrémentons notre variable de comptage, nous émettons l'événement. -Si nous déployons maintenant le contrat et appelons la fonction d'incrémentation, nous verrons que Remix l'affichera automatiquement si vous cliquez sur la nouvelle transaction dans un tableau nommé logs. +Si nous déployons maintenant le contrat et appelons la fonction d'incrémentation, nous verrons que Remix l'affichera automatiquement si vous cliquez sur la nouvelle transaction dans un tableau nommé « logs ». -![Remixer une capture d'écran](./remix-screenshot.png) +![Capture d'écran de Remix](./remix-screenshot.png) -Les journaux sont vraiment utiles pour déboguer vos contrats intelligents, mais ils sont également importants si vous construisez des applications utilisées par différentes personnes et facilitent les analyses du suivi et de la compréhension de l'utilisation de votre contrat intelligent. Les journaux générés par les transactions sont affichés dans les explorateurs de blocs populaires et vous pouvez également les utiliser par exemple pour créer des scripts hors chaîne pour surveiller des événements spécifiques et prendre des mesures lorsqu'ils se produisent. +Les journaux sont très utiles pour déboguer vos contrats intelligents, mais ils sont aussi importants si vous créez des applications utilisées par différentes personnes, car ils facilitent l'analyse pour suivre et comprendre comment votre contrat intelligent est utilisé. Les journaux générés par les transactions sont affichés dans les explorateurs de blocs populaires et vous pouvez également, par exemple, les utiliser pour créer des scripts hors chaîne pour surveiller des événements spécifiques et prendre des mesures lorsqu'ils se produisent. diff --git a/public/content/translations/fr/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md b/public/content/translations/fr/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md index 0da5ee8fa9f..69f47061cc4 100644 --- a/public/content/translations/fr/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md +++ b/public/content/translations/fr/developers/tutorials/merkle-proofs-for-offline-data-integrity/index.md @@ -1,9 +1,8 @@ --- -title: Preuves de Merkle relatives à l'intégrité des données hors ligne -description: Assurer l'intégrité des données en chaîne pour les données stockées, principalement, hors chaîne +title: "Preuves de Merkle relatives à l'intégrité des données hors ligne" +description: "Assurer l'intégrité des données en chaîne pour les données qui sont stockées, principalement, hors chaîne" author: Ori Pomerantz -tags: - - "stockage" +tags: [ "stockage" ] skill: advanced lang: fr published: 2021-12-30 @@ -13,31 +12,31 @@ published: 2021-12-30 Idéalement, nous souhaiterions tout conserver dans le stockage Ethereum, qui est lui-même stocké sur des milliers d'ordinateurs et bénéficie ainsi d'une disponibilité extrêmement élevée (les données ne peuvent pas être censurées) et d'une grande intégrité (les données ne peuvent pas être modifiées de manière non autorisée) sachant, cependant, que stocker un mot de 32 octets coûte normalement 20 000 gaz. Ce que je vais écrire ici aurait un coût équivalent à 6,60 $. À 21 cents par octet, c'est trop cher pour beaucoup d'utilisations. -Pour résoudre ce problème, l'écosystème Ethereum a développé [de nombreuses façons alternatives de stocker des données d'une manière décentralisée](/developers/docs/storage/). Habituellement, elles impliquent un compromis entre disponibilité et prix. Toutefois, l’intégrité est généralement assurée. +Pour résoudre ce problème, l'écosystème Ethereum a développé [de nombreuses façons alternatives de stocker des données de manière décentralisée](/developers/docs/storage/). Habituellement, elles impliquent un compromis entre disponibilité et prix. Toutefois, l’intégrité est généralement assurée. -Dans cet article, vous apprendrez **comment** assurer l'intégrité des données sans avoir à les stocker sur la blockchain, en utilisant [les preuves de Merkle](https://computersciencewiki.org/index.php/Merkle_proof). +Dans cet article, vous apprendrez **comment** garantir l'intégrité des données sans les stocker sur la blockchain, en utilisant les [preuves de Merkle](https://computersciencewiki.org/index.php/Merkle_proof). ## Comment ça marche ? {#how-does-it-work} -En théorie, nous pourrions simplement stocker le hachage des données de la chaîne et envoyer toutes les données dans les transactions qui en ont besoin. Cependant, cela reste encore trop cher. Un octet de données lors d'une transaction a un coût d'environ 16 gaz, soit environ un demi-cent actuellement ou environ 5 $ par kilo-octets. À 5 000 $ le mégaoctet, c'est encore trop cher pour de nombreuses utilisations même sans le coût supplémentaire de hachage des données. +En théorie, nous pourrions simplement stocker le hachage des données en chaîne, et envoyer toutes les données dans les transactions qui le requièrent. Cependant, cela reste encore trop cher. Un octet de données lors d'une transaction a un coût d'environ 16 gaz, soit environ un demi-cent actuellement ou environ 5 $ par kilo-octets. À 5 000 $ le mégaoctet, c'est encore trop cher pour de nombreuses utilisations même sans le coût supplémentaire de hachage des données. La solution est de hacher de façon répétée différents sous-ensembles de données. Pour les données que vous n'avez pas besoin d'envoyer, vous pouvez ainsi juste envoyer un hachage. Vous pouvez le faire en utilisant un arbre de Merkle, une structure de données en arborescence où chaque nœud est un hachage des nœuds en dessous : ![Arbre de Merkle](tree.png) -Le hachage racine est la seule partie qui doit être stockée dans la chaîne. Pour prouver une certaine valeur, vous fournissez tous les hachages qui doivent être combinés avec elle pour obtenir la racine. Par exemple, pour prouver `C` vous fournissez `D`, `H(A-B)`, et `H(E-H)`. +Le hachage racine est la seule partie qui doit être stockée en chaîne. Pour prouver une certaine valeur, vous fournissez tous les hachages qui doivent être combinés avec elle pour obtenir la racine. Par exemple, pour prouver `C`, vous fournissez `D`, `H(A-B)` et `H(E-H)`. ![Preuve de la valeur de C](proof-c.png) ## Implémentation {#implementation} -[Le code échantillon est fourni ici](https://github.com/qbzzt/merkle-proofs-for-offline-data-integrity). +[L'exemple de code est fourni ici](https://github.com/qbzzt/merkle-proofs-for-offline-data-integrity). -### Code hors chaîne {#off-chain-code} +### Code hors chaîne {#offchain-code} Dans cet article, nous utilisons JavaScript pour les calculs hors chaîne. La plupart des applications décentralisées ont leur composant hors chaîne en JavaScript. -#### Création de la racine Merkle {#creating-the-merkle-root} +#### Création de la racine de Merkle {#creating-the-merkle-root} Premièrement, nous devons apporter la racine Merkle à la chaîne. @@ -45,19 +44,19 @@ Premièrement, nous devons apporter la racine Merkle à la chaîne. const ethers = require("ethers") ``` -[Nous utilisons la fonction de hachage du paquet ethers](https://docs.ethers.io/v5/api/utils/hashing/#utils-keccak256). +[Nous utilisons la fonction de hachage du package ethers](https://docs.ethers.io/v5/api/utils/hashing/#utils-keccak256). ```javascript -// The raw data whose integrity we have to verify. The first two bytes a -// are a user identifier, and the last two bytes the amount of tokens the -// user owns at present. +// Les données brutes dont nous devons vérifier l'intégrité. Les deux premiers octets +// sont un identifiant d'utilisateur, et les deux derniers octets le montant de jetons que +// l'utilisateur possède actuellement. const dataArray = [ 0x0bad0010, 0x60a70020, 0xbeef0030, 0xdead0040, 0xca110050, 0x0e660060, 0xface0070, 0xbad00080, 0x060d0091, ] ``` -Encoder chaque entrée sur un seul entier de 256 bits donne un code moins lisible que JSON, par exemple. Cependant, cela signifie un traitement beaucoup moins important pour récupérer les données contenues dans le contrat et ainsi, des frais de gaz moins élevés. [Vous pouvez lire le JSON sur la chaîne](https://github.com/chrisdotn/jsmnSol), c'est juste une mauvaise idée si celà peut-être évité. +Encoder chaque entrée sur un seul entier de 256 bits donne un code moins lisible que JSON, par exemple. Cependant, cela signifie un traitement beaucoup moins important pour récupérer les données contenues dans le contrat et ainsi, des frais de gaz moins élevés. [Vous pouvez lire du JSON en chaîne](https://github.com/chrisdotn/jsmnSol), mais c'est une mauvaise idée si c'est évitable. ```javascript // The array of hash values, as BigInts @@ -67,22 +66,26 @@ const hashArray = dataArray Dans notre cas et pour commencer, nos données ont une valeur de 256 bits. Ainsi, aucun traitement n'est nécessaire. Si nous utilisons une structure de données plus compliquée, comme des chaînes de caractères, nous devons nous assurer de d'abord hacher les données pour obtenir un tableau de hachages. Notez que c'est aussi parce que nous ne nous soucions pas de savoir que les utilisateurs connaissent les informations des autres utilisateurs. Sinon, nous aurions dû réaliser un hachage de sorte que l'utilisateur 1 ne connaisse pas la valeur pour l'utilisateur 0, que l'utilisateur 2 ne connaisse pas la valeur pour l'utilisateur 3, etc. ```javascript -// Convert between the string the hash function expects and the -// BigInt we use everywhere else. +// Convertit entre la chaîne de caractères attendue par la fonction de hachage et le +// BigInt que nous utilisons partout ailleurs. const hash = (x) => BigInt(ethers.utils.keccak256("0x" + x.toString(16).padStart(64, 0))) ``` -La fonction de hachage des ethers s'attend à recevoir une chaîne JavaScript avec un nombre hexadécimal, tel que `0x60A7` et renvoie une autre chaîne de structure identique. Cependant, pour le reste du code, il est plus facile d'utiliser `BigInt` ainsi, nous convertissons en une chaîne hexadécimale et inversement. +La fonction de hachage d'ethers s'attend à recevoir une chaîne de caractères JavaScript avec un nombre hexadécimal, tel que `0x60A7`, et répond avec une autre chaîne de caractères de même structure. Cependant, pour le reste du code, il est plus facile d'utiliser `BigInt`, nous convertissons donc en une chaîne hexadécimale et vice-versa. ```javascript -// Symmetrical hash of a pair so we won't care if the order is reversed. +// Hachage symétrique d'une paire pour que l'inversion de l'ordre n'ait pas d'importance. const pairHash = (a, b) => hash(hash(a) ^ hash(b)) ``` -Cette fonction est symétrique (hachage d'un [xor](https://en.wikipedia.org/wiki/Exclusive_or) b). Cela signifie que lorsque nous vérifions la preuve de Merkle, nous n'avons pas à nous soucier de savoir s'il faut mettre la valeur de la preuve avant ou après la valeur calculée. C'est ainsi que l'on vérifie la preuve de Merkle, donc moins nous devons le faire, mieux ce sera. +Cette fonction est symétrique (hachage de a [xor](https://en.wikipedia.org/wiki/Exclusive_or) b). Cela signifie que lorsque nous vérifions la preuve de Merkle, nous n'avons pas à nous soucier de savoir s'il faut mettre la valeur de la preuve avant ou après la valeur calculée. La vérification des preuves de Merkle se fait en chaîne, donc moins nous avons d'opérations à y effectuer, mieux c'est. -Attention : La cryptographie est plus complexe qu'il n'y paraît. La version initiale de cet article avait la fonction de hachage `hash(a^b)`. C'était une **mauvaise** idée car cela signifiait que si vous connaissiez les valeurs légitimes de `a` et `b` vous pouviez utiliser `b' = a^b^a` pour prouver la valeur désirée `a'`. Avec cette fonction, vous devriez calculer `b'` de telle sorte que `hash(a') ^ hash(b')` soit égal à une valeur connue (la prochaine branche sur le chemin vers la racine), ce qui est beaucoup plus difficile. +Attention : +La cryptographie est plus complexe qu'il n'y paraît. +La version initiale de cet article avait la fonction de hachage `hash(a^b)`. +C'était une **mauvaise** idée car cela signifiait que si vous connaissiez les valeurs légitimes de `a` et `b`, vous pouviez utiliser `b' = a^b^a'` pour prouver n'importe quelle valeur `a'` désirée. +Avec cette fonction, vous devriez calculer `b'` de telle sorte que `hash(a') ^ hash(b')` soit égal à une valeur connue (la branche suivante sur le chemin de la racine), ce qui est beaucoup plus difficile. ```javascript // The value to denote that a certain branch is empty, doesn't @@ -92,14 +95,14 @@ const empty = 0n Lorsque le nombre de valeurs n'est pas un nombre entier à deux chiffres, nous devons gérer les branches vides. Pour ce faire, le programme va mettre un zéro à la place. -![Arbre de Merkle avec branches manquantes](merkle-empty-hash.png) +![Arbre de Merkle avec des branches manquantes](merkle-empty-hash.png) ```javascript -// Calculate one level up the tree of a hash array by taking the hash of -// each pair in sequence +// Calcule un niveau supérieur de l'arbre d'un tableau de hachage en prenant le hachage de +// chaque paire en séquence const oneLevelUp = (inputArray) => { var result = [] - var inp = [...inputArray] // To avoid over writing the input // Add an empty value if necessary (we need all the leaves to be // paired) + var inp = [...inputArray] // Pour éviter d'écraser l'entrée // Ajoute une valeur vide si nécessaire (nous avons besoin que toutes les feuilles soient // appariées) if (inp.length % 2 === 1) inp.push(empty) @@ -110,13 +113,13 @@ const oneLevelUp = (inputArray) => { } // oneLevelUp ``` -Cette fonction « escalade » un niveau dans l'arbre de Merkle en hachant les paires de valeurs de la couche actuelle. Notez que ce n'est pas l'implémentation la plus efficace, nous aurions pu éviter de copier l'entrée et juste ajouter `hashEmpty` lorsque cela est approprié dans la boucle, mais ce code est optimisé pour être plus lisible. +Cette fonction « escalade » un niveau dans l'arbre de Merkle en hachant les paires de valeurs de la couche actuelle. Notez que ce n'est pas l'implémentation la plus efficace, nous aurions pu éviter de copier l'entrée et juste ajouter `hashEmpty` lorsque cela est approprié dans la boucle, mais ce code est optimisé pour la lisibilité. ```javascript const getMerkleRoot = (inputArray) => { var result - result = [...inputArray] // Climb up the tree until there is only one value, that is the // root. // // If a layer has an odd number of entries the // code in oneLevelUp adds an empty value, so if we have, for example, // 10 leaves we'll have 5 branches in the second layer, 3 // branches in the third, 2 in the fourth and the root is the fifth + result = [...inputArray] // Remonte l'arbre jusqu'à ce qu'il n'y ait plus qu'une seule valeur, qui est la // racine. // // Si une couche a un nombre impair d'entrées, le // code dans oneLevelUp ajoute une valeur vide, donc si nous avons, par exemple, // 10 feuilles, nous aurons 5 branches dans la deuxième couche, 3 // branches dans la troisième, 2 dans la quatrième et la racine est la cinquième while (result.length > 1) result = oneLevelUp(result) @@ -126,27 +129,27 @@ const getMerkleRoot = (inputArray) => { Pour obtenir la racine, escaladez jusqu'à ce qu'il ne reste qu'une seule valeur. -#### Créer une preuve de Merkle {#creating-a-merkle-proof} +#### Création d'une preuve de Merkle {#creating-a-merkle-proof} Une preuve de Merkle est l'ensemble des valeurs à hacher ensemble avec la valeur prouvée pour récupérer la racine de Merkle. La valeur à prouver est souvent disponible à partir d'autres données. Je préfère ainsi la fournir séparément plutôt que dans le cadre du code. ```javascript -// A merkle proof consists of the value of the list of entries to -// hash with. Because we use a symmetrical hash function, we don't -// need the item's location to verify the proof, only to create it +// Une preuve de Merkle consiste en la valeur de la liste des entrées à +// hacher. Comme nous utilisons une fonction de hachage symétrique, nous n'avons pas +// besoin de l'emplacement de l'élément pour vérifier la preuve, seulement pour la créer const getMerkleProof = (inputArray, n) => {     var result = [], currentLayer = [...inputArray], currentN = n -    // Until we reach the top +    // Jusqu'à ce que nous atteignons le sommet     while (currentLayer.length > 1) { -        // No odd length layers +        // Pas de couches de longueur impaire         if (currentLayer.length % 2)             currentLayer.push(empty)         result.push(currentN % 2 -               // If currentN is odd, add with the value before it to the proof +               // Si currentN est impair, l'ajouter à la preuve avec la valeur qui le précède             ? currentLayer[currentN-1] -               // If it is even, add the value after it +               // S'il est pair, ajouter la valeur qui le suit             : currentLayer[currentN+1]) ``` @@ -154,16 +157,16 @@ const getMerkleProof = (inputArray, n) => { Nous hachons `(v[0],v[1])`, `(v[2],v[3])`, etc. Ainsi, pour les valeurs uniques, nous avons besoin de la suivante, pour les valeurs impaires de la précédente. ```javascript -        // Move to the next layer up +        // Passer à la couche supérieure         currentN = Math.floor(currentN/2)         currentLayer = oneLevelUp(currentLayer) -    }   // while currentLayer.length > 1 +    }   // tant que currentLayer.length > 1     return result }   // getMerkleProof ``` -### Code en chaîne {#on-chain-code} +### Code en chaîne {#onchain-code} Enfin, nous avons le code qui vérifie la preuve. Le code en chaîne est écrit en [Solidity](https://docs.soliditylang.org/en/v0.8.11/). L'optimisation est beaucoup plus importante ici parce que les frais en gaz sont relativement élevés. @@ -174,7 +177,7 @@ pragma solidity ^0.8.0; import "hardhat/console.sol"; ``` -J'ai écrit ceci en utilisant l'environnement de développement [Hardhat](https://hardhat.org/) qui nous permet d'avoir [une sortie console de Solidity](https://hardhat.org/docs/cookbook/debug-logs) durant le développement. +J'ai écrit ceci en utilisant l'[environnement de développement Hardhat](https://hardhat.org/), qui nous permet d'avoir une [sortie de console depuis Solidity](https://hardhat.org/docs/cookbook/debug-logs) pendant le développement. ```solidity @@ -185,15 +188,15 @@ contract MerkleProof {       return merkleRoot;     } -    // Extremely insecure, in production code access to -    // this function MUST BE strictly limited, probably to an -    // owner +    // Extrêmement peu sûr, dans le code de production, l'accès à +    // cette fonction DOIT ÊTRE strictement limité, probablement à un +    // propriétaire     function setRoot(uint _merkleRoot) external {       merkleRoot = _merkleRoot;     }   // setRoot ``` -Définissez et obtenez des fonctions pour la racine de Merkle. Laisser tout le monde mettre à jour la racine de Merkle est une _très mauvaise idée_ dans un système en production. Je le fais ici par souci de simplicité pour l'exemple de code. **Ne le faites pas sur un système où l'intégrité des données importe réellement**. +Définissez et obtenez des fonctions pour la racine de Merkle. Laisser tout le monde mettre à jour la racine de Merkle est une _très mauvaise idée_ dans un système de production. Je le fais ici par souci de simplicité pour l'exemple de code. **Ne le faites pas sur un système où l'intégrité des données importe réellement**. ```solidity     function hash(uint _a) internal pure returns(uint) { @@ -205,12 +208,12 @@ Définissez et obtenez des fonctions pour la racine de Merkle. Laisser tout le m     } ``` -Cette fonction génère un hachage de la paire. Il s'agit juste de la traduction en Solidity du code JavaScript pour `hash` et `pairHash`. +Cette fonction génère un hachage de la paire. Il s'agit simplement de la traduction en Solidity du code JavaScript pour `hash` et `pairHash`. -**Remarque :** Ceci est un autre exemple d'optimisation pour une meilleure lisibilité. Si l'on s'en tient à [la définition de la fonction](https://www.tutorialspoint.com/solidity/solidity_cryptographic_functions.htm), il est possible de stocker les données sous la forme d'une valeur [`bytes32`](https://docs.soliditylang.org/en/v0.5.3/types.html#fixed-size-byte-arrays) et d'éviter les conversions. +**Remarque :** C'est un autre cas d'optimisation pour la lisibilité. D'après [la définition de la fonction](https://www.tutorialspoint.com/solidity/solidity_cryptographic_functions.htm), il pourrait être possible de stocker les données sous la forme d'une valeur [`bytes32`](https://docs.soliditylang.org/en/v0.5.3/types.html#fixed-size-byte-arrays) et d'éviter les conversions. ```solidity -    // Verify a Merkle proof +    // Vérifier une preuve de Merkle     function verifyProof(uint _value, uint[] calldata _proof)         public view returns (bool) {       uint temp = _value; @@ -223,19 +226,21 @@ Cette fonction génère un hachage de la paire. Il s'agit juste de la traduction       return temp == merkleRoot;     } -}  // MarkleProof +}  // MerkleProof ``` -En notation mathématique, la vérification par la preuve de Merkle ressemble à ceci : `H(proof_n, H(proof_n-1, H(proof_n-2, ... H(proof_1, H(proof_0, value))...)))`. Ce code l'implémente. +En notation mathématique, la vérification de la preuve de Merkle ressemble à ceci : `H(proof_n, H(proof_n-1, H(proof_n-2, ...` `H(proof_1, H(proof_0, value))...)))`. Ce code l'implémente. ## Les preuves de Merkle et les rollups ne font pas bon ménage {#merkle-proofs-and-rollups} Les preuves de Merkle ne fonctionnent pas bien avec les [rollups](/developers/docs/scaling/#rollups). La raison en est que les rollups écrivent toutes les données de transaction sur L1, mais le processus sur L2. Le coût d'envoi d'une preuve Merkle avec une transaction est en moyenne de 638 gaz par couche (actuellement, un octet de données d'appel coûte 16 gaz s'il n'est pas nul, et 4 s'il est nul). Si nous avons 1 024 mots de données, une preuve de Merkle nécessite dix couches, soit un total de 6 380 gaz. -En cherchant un exemple avec [Optimism](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m), écrire du gaz en L1 coûte environ 100 gwei et le gaz en L2 coûte 0,001 gwei (c'est le prix normal, il peut augmenter s'il y a congestion). Ainsi, pour le coût d'un gaz L1, nous pouvons dépenser cent mille gaz pour le traitement L2. En supposant que nous n'ayons pas écrasé le stockage, cela signifie que nous pouvons écrire environ cinq mots à stocker sur L2 pour le prix d'un gaz L1. Pour une seule preuve de Merkle, nous pouvons écrire l'intégralité des 1 024 mots sur le stockage (en supposant qu'ils peuvent être calculés sur la chaîne pour commencer, au lieu d'être fourni dans une transaction) et disposons toujours de la majeure partie du gaz restant. +Si l'on prend l'exemple d'[Optimism](https://public-grafana.optimism.io/d/9hkhMxn7z/public-dashboard?orgId=1&refresh=5m), l'écriture du gaz L1 coûte environ 100 gwei et le gaz L2 coûte 0,001 gwei (c'est le prix normal, il peut augmenter en cas de congestion). Ainsi, pour le coût d'un gaz L1, nous pouvons dépenser cent mille gaz pour le traitement L2. En supposant que nous n'ayons pas écrasé le stockage, cela signifie que nous pouvons écrire environ cinq mots à stocker sur L2 pour le prix d'un gaz L1. Pour une seule preuve de Merkle, nous pouvons écrire les 1024 mots entiers dans le stockage (en supposant qu'ils peuvent être calculés en chaîne pour commencer, plutôt que fournis dans une transaction) et il nous restera toujours la plus grande partie du gaz. ## Conclusion {#conclusion} Dans la vie réelle, il se peut que vous n'ayez jamais à implémenter d'arbres de Merkle par vous-même. Il existe des bibliothèques bien connues et auditées que vous pouvez utiliser et, en général, il est préférable de ne pas implémenter des primitives cryptographiques par vous-même. Cependant, j'espère que, maintenant, vous comprendrez mieux les preuves de Merkle et que vous pourrez décider quand elles valent la peine d'être utilisées. -Notez que bien que les preuves de Merkle préservent _l'intégrité_, elles ne préservent pas _la disponibilité_. Savoir que personne d'autre ne peut se saisir de vos actifs est une petite consolation si le stockage de données décide d'en interdire l'accès et que vous ne pouvez pas non plus construire un arbre de Merkle pour y accéder. Ainsi, les arbres de Merkle sont mieux utilisés avec un type de stockage décentralisé, comme IPFS. +Notez que si les preuves de Merkle préservent l'_intégrité_, elles ne préservent pas la _disponibilité_. Savoir que personne d'autre ne peut se saisir de vos actifs est une petite consolation si le stockage de données décide d'en interdire l'accès et que vous ne pouvez pas non plus construire un arbre de Merkle pour y accéder. Ainsi, les arbres de Merkle sont mieux utilisés avec un type de stockage décentralisé, comme IPFS. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md b/public/content/translations/fr/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md index 6ad31d6699b..23692a3aa82 100644 --- a/public/content/translations/fr/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md +++ b/public/content/translations/fr/developers/tutorials/monitoring-geth-with-influxdb-and-grafana/index.md @@ -1,29 +1,27 @@ --- title: Surveillance de Geth avec InfluxDB et Grafana -description: +description: "Configurez la surveillance de votre nœud Geth avec InfluxDB et Grafana pour suivre les performances et identifier les problèmes." author: "Mario Havel" -tags: - - "clients" - - "nœuds" +tags: [ "clients", "nœuds" ] skill: intermediate lang: fr published: 2021-01-13 --- -Ce tutoriel vous aidera à mettre en place une surveillance de votre nœud Geth afin de mieux comprendre ses performances et identifier les problèmes potentiels. +Ce tutoriel vous aidera à mettre en place une surveillance de votre nœud Geth afin de mieux comprendre ses performances et d'identifier les problèmes potentiels. ## Prérequis {#prerequisites} -- Vous devriez déjà savoir exécuter une instance Geth. -- La plupart des étapes et des exemples étant réalisés pour l'environnement linux, la connaissance des bases sur terminal sera utile. -- Visionnez cette vidéo pour obtenir un aperçu de suite de métriques de Geth : [Surveillance d'une infrastructure Ethereum par Péter Szilágyi](https://www.youtube.com/watch?v=cOBab8IJMYI). +- Vous devriez déjà avoir une instance de Geth en cours d'exécution. +- La plupart des étapes et des exemples sont destinés à un environnement Linux ; des connaissances de base du terminal seront utiles. +- Découvrez cet aperçu vidéo de la suite de métriques de Geth : [Surveillance d'une infrastructure Ethereum par Péter Szilágyi](https://www.youtube.com/watch?v=cOBab8IJMYI). ## Pile de surveillance {#monitoring-stack} -Un client Ethereum collecte de nombreuses données qui peuvent être lues sous la forme d'une base de données chronologique. Pour faciliter la surveillance, vous pouvez l'intégrer dans le logiciel de visualisation des données. Plusieurs options sont disponibles : +Un client Ethereum collecte de nombreuses données qui peuvent être lues sous la forme d'une base de données chronologique. Pour faciliter la surveillance, vous pouvez intégrer ces données dans un logiciel de visualisation. Plusieurs options sont disponibles : -- [Prometheus](https://prometheus.io/) (modèle de retrait) -- [InfluxDB](https://www.influxdata.com/get-influxdb/) (modèle d'ajout) +- [Prometheus](https://prometheus.io/) (modèle pull) +- [InfluxDB](https://www.influxdata.com/get-influxdb/) (modèle push) - [Telegraf](https://www.influxdata.com/get-influxdb/) - [Grafana](https://www.grafana.com/) - [Datadog](https://www.datadoghq.com/) @@ -31,11 +29,12 @@ Un client Ethereum collecte de nombreuses données qui peuvent être lues sous l Il existe également [Geth Prometheus Exporter](https://github.com/hunterlong/gethexporter), une option préconfigurée avec InfluxDB et Grafana. -Dans ce tutoriel, nous allons configurer votre client Geth pour pousser des données sur InfluxDB afin de créer une base de données, et Grafana pour créer une visualisation graphique des données. Réaliser cela manuellement vous aidera à mieux comprendre le processus, à le modifier et à le déployer dans différents environnements. +Dans ce tutoriel, nous allons configurer votre client Geth pour pousser des données sur InfluxDB afin de créer une base de données, et sur Grafana pour créer une visualisation graphique des données. Le faire manuellement vous aidera à mieux comprendre le processus, à le modifier et à le déployer dans différents environnements. -## Configuration de InfluxDB {#setting-up-influxdb} +## Configuration d'InfluxDB {#setting-up-influxdb} -Tout d'abord, téléchargeons et installons InfluxDB. Diverses options de téléchargement peuvent être trouvées sur la page des versions de [Influxdata](https://portal.influxdata.com/downloads/). Choisissez celles qui conviennent à votre environnement. Vous pouvez également l'installer depuis un [dépôt](https://repos.influxdata.com/). Par exemple dans la distribution basée sur Debian : +Tout d'abord, téléchargeons et installons InfluxDB. Vous trouverez plusieurs options de téléchargement sur la [page des versions d'Influxdata](https://portal.influxdata.com/downloads/). Choisissez celle qui convient à votre environnement. +Vous pouvez également l'installer depuis un [dépôt](https://repos.influxdata.com/). Par exemple, pour une distribution basée sur Debian : ``` curl -tlsv1.3 --proto =https -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add @@ -48,51 +47,53 @@ sudo systemctl start influxdb sudo apt install influxdb-client ``` -Après avoir installé InfluxDB avec succès, assurez-vous qu'il fonctionne en arrière-plan. Par défaut, il est accessible via `localhost:8086`. Avant d'utiliser le client `influx`, vous devez créer un nouvel utilisateur avec les privilèges d'administrateur. Cet utilisateur servira à la gestion de haut niveau, à la création de bases de données et d'utilisateurs. +Après avoir installé InfluxDB avec succès, assurez-vous qu'il fonctionne en arrière-plan. Par défaut, il est accessible à l'adresse `localhost:8086`. +Avant d'utiliser le client `influx`, vous devez créer un nouvel utilisateur avec des privilèges d'administrateur. Cet utilisateur servira à la gestion de haut niveau, à la création de bases de données et d'utilisateurs. ``` curl -XPOST "http://localhost:8086/query" --data-urlencode "q=CREATE USER username WITH PASSWORD 'password' WITH ALL PRIVILEGES" ``` -Maintenant, vous pouvez utiliser le client influx pour entrer [InfluxDB shell](https://docs.influxdata.com/influxdb/v1.8/tools/shell/) avec cet utilisateur. +Vous pouvez maintenant utiliser le client influx pour entrer dans le [shell InfluxDB](https://docs.influxdata.com/influxdb/v1.8/tools/shell/) avec cet utilisateur. ``` influx -username 'username' -password 'password' ``` -En communiquant directement avec InfluxDB dans son invite de commande, vous pouvez créer une base de données et un utilisateur pour les métriques Geth. +En communiquant directement avec InfluxDB dans son shell, vous pouvez créer une base de données et un utilisateur pour les métriques de Geth. ``` create database geth create user geth with password choosepassword ``` -Vérifiez les entrées créées avec : +Vérifiez les entrées créées avec : ``` show databases show users ``` -Quitter l’invite de commande InfluxDB. +Quittez le shell InfluxDB. ``` exit ``` -InfluxDB est en cours d'exécution et configuré pour stocker les métriques Geth. +InfluxDB est en cours d'exécution et configuré pour stocker les métriques de Geth. ## Préparation de Geth {#preparing-geth} -Après avoir configuré la base de données, nous devons activer la collecte des métriques dans Geth. Faites attention aux `OPTIONS METRICS ET STATS` dans `geth --help`. Plusieurs options peuvent y être trouvées. Dans ce cas, nous voulons que Geth envoie des données dans InfluxDB. La configuration de base spécifie le point de terminaison où InfluxDB est accessible ainsi que l'authentification pour la base de données. +Après avoir configuré la base de données, nous devons activer la collecte de métriques dans Geth. Faites attention aux `METRICS AND STATS OPTIONS` dans `geth --help`. Vous y trouverez plusieurs options ; dans notre cas, nous voulons que Geth pousse les données vers InfluxDB. +La configuration de base spécifie le point de terminaison où InfluxDB est joignable et l'authentification pour la base de données. ``` geth --metrics --metrics.influxdb --metrics.influxdb.endpoint "http://0.0.0.0:8086" --metrics.influxdb.username "geth" --metrics.influxdb.password "chosenpassword" ``` -Ces options peuvent être ajoutées à une commande démarrant le client ou enregistrées dans le fichier de configuration. +Ces options peuvent être ajoutées à une commande qui lance le client ou être enregistrées dans le fichier de configuration. -Vous pouvez vérifier que Geth envoie les données avec succès, par exemple en listant les paramètres dans la base de données. Dans l'invite de commande InfluxDB : +Vous pouvez vérifier que Geth pousse bien les données, par exemple en listant les métriques dans la base de données. Dans le shell InfluxDB : ``` use geth @@ -101,7 +102,8 @@ show measurements ## Configuration de Grafana {#setting-up-grafana} -L'étape suivante est l'installation de Grafana qui interprétera les données graphiquement. Suivez le processus d'installation au regard de votre environnement dans la documentation de Grafana. Assurez-vous d'installer la version OSS si vous ne voulez pas le contraire. Exemple d'étapes d'installation pour les distributions Debian en utilisant le dépôt : +L'étape suivante consiste à installer Grafana, qui interprétera les données graphiquement. Suivez le processus d'installation pour votre environnement dans la documentation de Grafana. Assurez-vous d'installer la version OSS si vous n'avez pas besoin d'une autre version. +Exemple d'étapes d'installation pour les distributions Debian à l'aide du dépôt : ``` curl -tlsv1.3 --proto =https -sL https://packages.grafana.com/gpg.key | sudo apt-key add - @@ -112,36 +114,38 @@ sudo systemctl enable grafana-server sudo systemctl start grafana-server ``` -Lorsque Grafana est en cours d'exécution, il devrait être accessible via `localhost:3000`. Utilisez votre navigateur préféré pour accéder à ce chemin, puis connectez-vous avec les identifiants par défaut (utilisateur : `admin` et mot de passe : `admin`). Lorsque vous y êtes invité, changez le mot de passe par défaut et enregistrez. +Lorsque Grafana est en cours d'exécution, il devrait être accessible à l'adresse `localhost:3000`. +Utilisez votre navigateur préféré pour accéder à ce chemin, puis connectez-vous avec les identifiants par défaut (utilisateur : `admin` et mot de passe : `admin`). Lorsque vous y êtes invité, changez le mot de passe par défaut et enregistrez. ![](./grafana1.png) -Vous serez redirigé vers la page d'accueil de Grafana. Tout d'abord, configurez vos données sources. Cliquez sur l'icône de configuration dans la barre de gauche et sélectionnez « Data sources ». +Vous serez redirigé vers la page d'accueil de Grafana. Tout d'abord, configurez vos données sources. Cliquez sur l'icône de configuration dans la barre de gauche et sélectionnez "Sources de données". ![](./grafana2.png) -Il n'y a pas encore de sources de données créées, cliquez sur « Add data source » pour en définir. +Aucune source de données n'a encore été créée, cliquez sur "Ajouter une source de données" pour en définir une. ![](./grafana3.png) -Pour cette configuration, sélectionnez « InfluxDB » et continuez. +Pour cette configuration, sélectionnez "InfluxDB" et continuez. ![](./grafana4.png) -La configuration des données sources est assez directe si vous utilisez des outils sur la même machine. Vous devez définir l'adresse InfluxDB et les détails pour accéder à la base de données. Reportez-vous à l'image ci-dessous. +La configuration de la source de données est assez simple si vous exécutez les outils sur la même machine. Vous devez définir l'adresse d'InfluxDB et les informations d'accès à la base de données. Reportez-vous à l'image ci-dessous. ![](./grafana5.png) -Si la configuration est complète et que InfluxDB est joignable, cliquez sur « Save and test » et attendez que la confirmation apparaisse. +Si tout est complet et qu'InfluxDB est accessible, cliquez sur "Enregistrer et tester" et attendez que la confirmation apparaisse. ![](./grafana6.png) -Grafana est maintenant configuré pour lire les données depuis InfluxDB. Maintenant, vous devez créer un tableau de bord qui les interpréter et les affichera. Les propriétés des tableaux de bord sont encodées en fichiers JSON qui peuvent être créés par n'importe qui et facilement importés. Dans la barre de gauche, cliquez sur « Create and Import ». +Grafana est maintenant configuré pour lire les données depuis InfluxDB. Vous devez maintenant créer un tableau de bord qui interprétera et affichera ces données. Les propriétés des tableaux de bord sont encodées dans des fichiers JSON qui peuvent être créés par n'importe qui et importés facilement. Dans la barre de gauche, cliquez sur "Créer et Importer". ![](./grafana7.png) -Pour un tableau de bord de surveillance Geth, copiez l'ID de [ce tableau de bord](https://grafana.com/grafana/dashboards/13877/) et collez-le dans « Import page » sur Grafana. Après avoir enregistré le tableau de bord, il devrait ressembler à ceci : +Pour un tableau de bord de surveillance Geth, copiez l'ID de [ce tableau de bord](https://grafana.com/grafana/dashboards/13877/) et collez-le dans la "Page d'importation" dans Grafana. Après avoir enregistré le tableau de bord, il devrait ressembler à ceci : ![](./grafana8.png) -Vous pouvez modifier vos tableaux de bord. Chaque panneau peut être modifié, déplacé, supprimé ou ajouté. Vous pouvez modifier vos configurations. C'est à vous ! Pour en savoir plus sur le fonctionnement des tableaux de bord, reportez-vous à la documentation de [Grafana](https://grafana.com/docs/grafana/latest/dashboards/). Vous pourriez également être intéressé par [Alerte](https://grafana.com/docs/grafana/latest/alerting/). Cela vous permet de configurer des notifications d'alerte lorsque les paramètres atteignent certaines valeurs. Différents canaux de communication sont pris en charge. +Vous pouvez modifier vos tableaux de bord. Chaque panneau peut être modifié, déplacé, supprimé ou ajouté. Vous pouvez modifier vos configurations. À vous de jouer ! Pour en savoir plus sur le fonctionnement des tableaux de bord, consultez la [documentation de Grafana](https://grafana.com/docs/grafana/latest/dashboards/). +Les [alertes](https://grafana.com/docs/grafana/latest/alerting/) pourraient également vous intéresser. Cela vous permet de configurer des notifications d'alerte pour les cas où les métriques atteignent certaines valeurs. Divers canaux de communication sont pris en charge. diff --git a/public/content/translations/fr/developers/tutorials/nft-minter/index.md b/public/content/translations/fr/developers/tutorials/nft-minter/index.md index 3c99a06a073..7ac28721c0b 100644 --- a/public/content/translations/fr/developers/tutorials/nft-minter/index.md +++ b/public/content/translations/fr/developers/tutorials/nft-minter/index.md @@ -1,14 +1,16 @@ --- title: Tutoriel pour frapper des NFT -description: Dans ce tutoriel, vous allez créer un générateur de NFT et apprendre à créer une application décentralisée dApp full-stack en reliant votre contrat intelligent à une interface React, à l'aide de MetaMask et d'autres outils Web3. +description: "Dans ce tutoriel, vous allez créer un générateur de NFT et apprendre à créer une application décentralisée dApp full-stack en reliant votre contrat intelligent à une interface React, à l'aide de MetaMask et d'autres outils Web3." author: "smudgil" tags: - - "solidity" - - "NFT" - - "alchemy" - - "contrats intelligents" - - "frontend" - - "Pinata" + [ + "solidité", + "NFT", + "alchemy", + "contrats intelligents", + "frontend", + "Pinata" + ] skill: intermediate lang: fr published: 2021-10-06 @@ -18,19 +20,19 @@ L'un des plus grands défis pour les développeurs venus du Web2 est de comprend En construisant un générateur de NFT — une interface simple où vous pouvez saisir un lien vers votre ressource numérique, un titre et une description — vous apprendrez à : -- Vous connecter à MetaMask via votre interface +- Vous connecter à MetaMask via votre projet en frontend - Appeler les méthodes du contrat intelligent depuis votre interface -- Signer les transactions à l'aide de MetaMask +- Signer des transactions à l'aide de MetaMask -Dans ce tutoriel, nous utiliserons [React](https://reactjs.org/) en tant que framework d'interface. Puisque ce tutoriel s'intéresse avant tout au développement Web3, nous ne nous attarderons pas à expliquer les bases de React. Au lieu de cela, nous nous concentrerons sur l'ajout de fonctionnalités à notre projet. +Dans ce tutoriel, nous utiliserons [React](https://react.dev/) comme framework frontend. Puisque ce tutoriel s'intéresse avant tout au développement Web3, nous ne nous attarderons pas à expliquer les bases de React. Au lieu de cela, nous nous concentrerons sur l'ajout de fonctionnalités à notre projet. -En prérequis, il vous faudra un niveau débutant en React — savoir comment fonctionnent les composants, les props, useState/useEffect, et les appels des fonctions de base. Si vous n'avez jamais entendu parler de ces termes auparavant, vous pouvez consulter ce [tutoriel d'introduction à React](https://reactjs.org/tutorial/tutorial.html). Pour les apprenants plus visuels, nous recommandons fortement cette excellente série vidéo [Full Modern React Tutorial](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d) par Net Ninja. +En prérequis, il vous faudra un niveau débutant en React — savoir comment fonctionnent les composants, les props, useState/useEffect, et les appels des fonctions de base. Si vous n'avez jamais entendu parler de l'un de ces termes auparavant, vous pouvez consulter ce [tutoriel d'introduction à React](https://react.dev/learn/tutorial-tic-tac-toe). Pour les apprenants plus visuels, nous recommandons vivement cette excellente série de vidéos [Full Modern React Tutorial](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d) par Net Ninja. Et si vous ne l'avez pas déjà fait, vous aurez certainement besoin d'un compte Alchemy pour terminer ce tutoriel ainsi que pour construire quoi que ce soit sur la blockchain. Créez un compte gratuit [ici](https://alchemy.com/). Sans plus attendre, commençons ! -## Introduction à la fabrication de NFT {#making-nfts-101} +## Création de NFT 101 {#making-nfts-101} Avant même de commencer à regarder du code, il est important de comprendre comment la fabrication d'un NFT fonctionne. Elle comporte deux étapes : @@ -38,36 +40,36 @@ Avant même de commencer à regarder du code, il est important de comprendre com La plus grande différence entre les deux normes de contrat intelligent NFT est que l'ERC-1155 est un standard multijeton et inclut la fonctionnalité de lot. Alors que l'ERC-721 est un standard à jeton unique et supporte donc uniquement le transfert d'un jeton à la fois. -### Appeler la fonction de « frappe » (mint) {#minting-function} +### Appeler la fonction de frappe {#minting-function} -Habituellement, cette fonction de frappe nécessite que vous passiez deux variables en paramètres. Tout d'abord le `recipient`, qui spécifie l'adresse qui recevra votre NFT fraîchement frappé. Et la seconde qui est le `tokenURI`du NFT : une chaîne de caractères qui pointe sur un document JSON décrivant les métadonnées du NFT. +Généralement, cette fonction de frappe vous demande de transmettre deux variables en tant que paramètres, premièrement le `destinataire`, qui spécifie l'adresse qui recevra votre NFT fraîchement frappé, et deuxièmement le `tokenURI` du NFT, une chaîne qui renvoie à un document JSON décrivant les métadonnées du NFT. -Les métadonnées d'un NFT sont ce qui lui donne vie, lui permettant d'avoir des propriétés configurables, comme un nom, une description, une image et d'autres attributs. Voici [un exemple de tokenURI](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2) qui contient les métadonnées d'un NFT. +Les métadonnées d'un NFT sont ce qui lui donne vie, lui permettant d'avoir des propriétés configurables, comme un nom, une description, une image et d'autres attributs. Voici [un exemple de tokenURI](https://gateway.pinata.cloud/ipfs/QmSvBcb4tjdFpajGJhbFAWeK3JAxCdNQLQtr6ZdiSi42V2), qui contient les métadonnées d'un NFT. Dans ce tutoriel, nous allons nous concentrer sur la deuxième partie, en appelant la fonction existante de frappe d'un contrat intelligent de type NFT avec notre interface React. -[Voici un lien](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE) vers le contrat intelligent ERC-721 NFT que nous allons appeler dans ce tutoriel. Si vous souhaitez apprendre comment nous l'avons fait, nous vous recommandons fortement de consulter notre autre tutoriel, ["Comment créer un NFT"](https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft). +[Voici un lien](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE) vers le contrat intelligent de NFT ERC-721 que nous appellerons dans ce tutoriel. Si vous souhaitez apprendre comment nous l'avons créé, nous vous recommandons vivement de consulter notre autre tutoriel, ["Comment créer un NFT"](https://www.alchemy.com/docs/how-to-create-an-nft). Bien, maintenant que nous comprenons comment la fabrication de NFT fonctionne, clonons nos fichiers et démarrons  ! ## Cloner les fichiers de démarrage {#clone-the-starter-files} -Tout d'abord, rendez-vous sur le dépôt GitHub [nft-minter-tutorial](https://github.com/alchemyplatform/nft-minter-tutorial) pour obtenir les fichiers de démarrage de ce projet. Clonez ce dépôt dans votre environnement local.= +Tout d'abord, accédez au [dépôt GitHub nft-minter-tutorial](https://github.com/alchemyplatform/nft-minter-tutorial) pour obtenir les fichiers de démarrage de ce projet. Clonez ce dépôt dans votre environnement local. -Lorsque vous ouvrez ce dépôt `nft-minter-tutorial` , vous remarquerez qu'il contient deux dossiers : `minter-starter-files` et `nft-minter`. +Lorsque vous ouvrez ce dépôt `nft-minter-tutorial` cloné, vous remarquerez qu'il contient deux dossiers : `minter-starter-files` et `nft-minter`. -- `minter-starter-files` contient les fichiers de démarrage (essentiellement l'interface utilisateur en React) pour ce projet. Dans ce tutoriel, **nous travaillerons dans ce répertoire**. Au fur et à mesure, vous apprendrez à donner vie à cette interface utilisateur en la connectant à votre portefeuille Ethereum et à un contrat intelligent NFT. -- `nft-minter` contient l'intégralité du tutoriel et vous servira de **référence** **si vous êtes coincé.** +- `minter-starter-files` contient les fichiers de démarrage (essentiellement l'interface utilisateur React) de ce projet. Dans ce tutoriel, **nous travaillerons dans ce répertoire**, où vous apprendrez à donner vie à cette interface utilisateur en la connectant à votre portefeuille Ethereum et à un contrat intelligent de NFT. +- `nft-minter` contient le tutoriel entièrement terminé et est là pour vous servir de **référence** **si vous êtes bloqué.** -Ensuite, ouvrez votre copie de `minter-starter-files` dans votre éditeur de code, puis naviguez dans votre dossier `src`. +Ensuite, ouvrez votre copie de `minter-starter-files` dans votre éditeur de code, puis accédez à votre dossier `src`. -Tout le code que nous allons écrire restera dans le dossier `src`. Nous allons modifier le composant `Minter.js` et écrire des fichiers javascript supplémentaires pour ajouter des fonctionnalités à notre projet Web3. +Tout le code que nous allons écrire se trouvera dans le dossier `src`. Nous allons modifier le composant `Minter.js` et écrire des fichiers javascript supplémentaires pour donner à notre projet des fonctionnalités Web3. -## Étape 2 : Vérifier nos fichiers de démarrage {#step-2-check-out-our-starter-files} +## Étape 2 : consultez nos fichiers de démarrage {#step-2-check-out-our-starter-files} Avant de commencer à coder, il est important de connaître ce qui est déjà fourni dans les fichiers de base. -### Faites tourner votre projet de React {#get-your-react-project-running} +### Faire fonctionner votre projet React {#get-your-react-project-running} Commençons par exécuter le projet React dans notre navigateur. La beauté de React est qu'une fois que notre projet est en cours d'exécution dans notre navigateur, toutes les modifications que nous sauvegardons seront mises à jour en direct dans notre navigateur. @@ -90,14 +92,14 @@ Si vous essayez de cliquer sur les boutons « Connect Wallet » (connecter le po ### Le composant Minter.js {#minter-js} -**REMARQUE :** Assurez-vous d'être dans le dossier `minter-starter-files` et non le dossier `nft-minter` ! +**REMARQUE :** Assurez-vous d'être dans le dossier `minter-starter-files` et non dans le dossier `nft-minter` ! -Revenons dans le dossier `src` de notre éditeur et ouvrons le fichier `Minter.js`. Il est très important de comprendre tout ce qui se trouve dans ce fichier, car c'est le composant principal de React sur lequel nous allons travailler. +Retournons dans le dossier `src` de notre éditeur et ouvrons le fichier `Minter.js`. Il est très important de comprendre tout ce qui se trouve dans ce fichier, car c'est le composant principal de React sur lequel nous allons travailler. En haut de ce fichier, nous avons nos variables d'état que nous mettrons à jour après des événements spécifiques. ```javascript -//State variables +//Variables d'état const [walletAddress, setWallet] = useState("") const [status, setStatus] = useState("") const [name, setName] = useState("") @@ -105,135 +107,135 @@ const [description, setDescription] = useState("") const [url, setURL] = useState("") ``` -Vous n'avez jamais entendu parler de variables d'état React ou de hooks d'état ? Jetez un œil à cette [documentation](https://reactjs.org/docs/hooks-state.html). +Vous n'avez jamais entendu parler de variables d'état React ou de hooks d'état ? Consultez [cette](https://legacy.reactjs.org/docs/hooks-state.html) documentation. Voici ce que chacune des variables représente : -- `walletAddress` - une chaîne de caractère qui stocke l'adresse du portefeuille de l'utilisateur -- `status` - une chaîne de caractère qui contient un message à afficher en bas de l'interface utilisateur -- `name` - une chaîne de caractère qui stocke le nom du NFT -- `description` - une chaîne de caractère qui stocke la description du NFT -- `url` - une chaîne de caractères qui est un lien vers l'actif numérique du NFT +- `walletAddress` : une chaîne qui stocke l'adresse du portefeuille de l'utilisateur +- `status` : une chaîne qui contient un message à afficher en bas de l'interface utilisateur +- `name` : une chaîne qui stocke le nom du NFT +- `description` : une chaîne qui stocke la description du NFT +- `url` : une chaîne qui est un lien vers l'actif numérique du NFT -Après les variables d'état, vous verrez trois fonctions non implémentées : `useEffect`, `connectWalletPressed`, et `onMintPressed`. Vous remarquerez que toutes ces fonctions sont `async`, c'est parce que nous allons faire des appels d'API asynchrones ! Leurs noms correspondent à leurs fonctionnalités : +Après les variables d'état, vous verrez trois fonctions non implémentées : `useEffect`, `connectWalletPressed` et `onMintPressed`. Vous remarquerez que toutes ces fonctions sont `async`, c'est parce que nous y effectuerons des appels d'API asynchrones ! Leurs noms correspondent à leurs fonctionnalités : ```javascript useEffect(async () => { - //TODO: implement + //TODO: à implémenter }, []) const connectWalletPressed = async () => { - //TODO: implement + //TODO: à implémenter } const onMintPressed = async () => { - //TODO: implement + //TODO: à implémenter } ``` -- [`useEffect`](https://reactjs.org/docs/hooks-effect.html) - Il s'agit d'un hook React qui est appelé après que votre composant est affiché. Parce qu'une prop tableau vide `[]` lui est passée (voir la ligne 3), elle ne sera appelée qu'au _premier_ affichage du composant. Ici, nous appellerons notre écouteur de portefeuille et une autre fonction de portefeuille pour mettre à jour notre interface utilisateur afin de déterminer si un portefeuille est déjà connecté. -- `connectWalletPressed` - cette fonction sera appelée pour connecter le portefeuille MetaMask de l'utilisateur à notre dApp. -- `onMintPressed` - cette fonction sera appelée pour frapper le NFT de l'utilisateur. +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) : il s'agit d'un hook React qui est appelé après le rendu de votre composant. Comme il reçoit une prop de tableau vide `[]` (voir ligne 3), il ne sera appelé que lors du _premier_ rendu du composant. Ici, nous appellerons notre écouteur de portefeuille et une autre fonction de portefeuille pour mettre à jour notre interface utilisateur afin de déterminer si un portefeuille est déjà connecté. +- `connectWalletPressed` : cette fonction sera appelée pour connecter le portefeuille MetaMask de l'utilisateur à notre dapp. +- `onMintPressed` : cette fonction sera appelée pour frapper le NFT de l'utilisateur. -Vers la fin de ce fichier, nous avons l'interface utilisateur de notre composant. Si vous scannez ce code attentivement, vous remarquerez que nous mettons à jour nos variables d'état `url`, `name`, et `description` lorsque le contenu entré dans leurs champs de texte change. +Vers la fin de ce fichier, nous avons l'interface utilisateur de notre composant. Si vous examinez attentivement ce code, vous remarquerez que nous mettons à jour nos variables d'état `url`, `name` et `description` lorsque l'entrée dans les champs de texte correspondants change. -Vous verrez également que `connectWalletPressed` et `onMintPressed` sont appelées lorsque les boutons portant les IDs respectifs `mintButton` et `walletButton` sont cliqués. +Vous verrez également que `connectWalletPressed` et `onMintPressed` sont appelées lorsque les boutons avec les ID `mintButton` et `walletButton` sont cliqués respectivement. ```javascript -//the UI of our component +//L'interface utilisateur de notre composant return (


-

🧙‍♂️ Alchemy NFT Minter

+

🧙‍♂️ Outil de frappe de NFT Alchemy

- Simply add your asset's link, name, and description, then press "Mint." + Ajoutez simplement le lien, le nom et la description de votre actif, puis appuyez sur "Frapper le NFT".

-

🖼 Link to asset:

+

🖼 Lien vers l'actif :

setURL(event.target.value)} /> -

🤔 Name:

+

🤔 Nom :

setName(event.target.value)} /> -

✍️ Description:

+

✍️ Description :

setDescription(event.target.value)} />

{status}

-
+ ) ``` Enfin, occupons-nous de l'endroit où ajouter ce composant Minter. -Si vous ouvrez le fichier `App.js`, qui est le composant principal en React agissant comme un conteneur pour tous les autres composants, vous verrez que notre composant Minter est injecté à la ligne 7. +Si vous allez dans le fichier `App.js`, qui est le composant principal de React et qui sert de conteneur pour tous les autres composants, vous verrez que notre composant Minter est injecté à la ligne 7. -**Dans ce tutoriel, nous allons seulement modifier le fichier `Minter.js` et ajouter des fichiers dans notre dossier `src`.** +**Dans ce tutoriel, nous ne modifierons que le fichier `Minter.js` et ajouterons des fichiers dans notre dossier `src`.** Maintenant que nous comprenons ce avec quoi nous travaillons, mettons en place notre portefeuille Ethereum ! -## Configurez votre portefeuille Ethereum {#set-up-your-ethereum-wallet} +## Configurer votre portefeuille Ethereum {#set-up-your-ethereum-wallet} Pour que les utilisateurs puissent interagir avec votre contrat intelligent, ils devront connecter leur portefeuille Ethereum à votre dApp. -### Téléchargez MetaMask {#download-metamask} +### Télécharger MetaMask {#download-metamask} -Pour ce tutoriel, nous utiliserons MetaMask, un portefeuille virtuel utilisable dans le navigateur servant à gérer les adresses Ethereum. Si vous voulez en savoir plus sur le fonctionnement des transactions sur Ethereum, consultez [cette page](/developers/docs/transactions/). +Pour ce tutoriel, nous allons utiliser MetaMask, un portefeuille virtuel intégré au navigateur, servant à gérer les adresses de votre compte Ethereum. Si vous voulez en savoir plus sur le fonctionnement des transactions sur Ethereum, consultez [cette page](/developers/docs/transactions/). Vous pouvez télécharger et créer un compte MetaMask gratuitement [ici](https://metamask.io/download). Lorsque vous créez un compte, ou si vous en avez déjà un, assurez-vous de basculer sur « Réseau de test Ropsten » en haut à droite \(afin de ne pas utiliser d'argent réel\). -### Ajoutez de l'ether depuis un Robinet {#add-ether-from-faucet} +### Ajouter de l'ether depuis un robinet {#add-ether-from-faucet} -Afin de frapper nos NFT (ou de signer des transactions sur la blockchain Ethereum), nous aurons besoin de faux Eth. Pour obtenir des ETH, vous pouvez vous rendre sur le [robinet Ropsten](https://faucet.ropsten.be/) et entrer votre adresse Ropsten, puis cliquer sur « Send Ropsten ETH. » Vous devriez voir les ETH dans votre compte MetaMask peu de temps après ! +Afin de frapper nos NFT (ou de signer des transactions sur la blockchain Ethereum), nous aurons besoin de faux Eth. Pour obtenir de l'ETH, vous pouvez vous rendre sur le [robinet Ropsten](https://faucet.ropsten.be/), saisir l'adresse de votre compte Ropsten, puis cliquer sur « Send Ropsten Eth ». Vous devriez voir les ETH dans votre compte MetaMask peu de temps après ! -### Vérifiez votre solde {#check-your-balance} +### Vérifier votre solde {#check-your-balance} -Pour revérifier que votre solde est correct, faisons une requête [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) en utilisant [l'outil Alchemy Composer](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Cela va retourner la quantité d'ETH que contient votre portefeuille. Après avoir entré l'adresse de votre compte MetaMask et cliqué sur « Send Request », vous devriez voir une réponse comme celle-ci : +Pour vérifier que notre solde est bien là, faisons une requête [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) en utilisant l'[outil de composition d'Alchemy](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Cela va retourner la quantité d'ETH que contient votre portefeuille. Après avoir entré l'adresse de votre compte MetaMask et cliqué sur « Send Request », vous devriez voir une réponse comme celle-ci : ```text {"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} ``` -**REMARQUE :** Ce résultat est en wei et non pas en ETH. Le wei est utilisé comme la plus petite unité d'ether. La conversion de wei vers eth est : 1 eth = 10¹⁸ wei. Donc si on convertit 0xde0b6b3a7640000 en nombre décimal, nous obtenons 1\*10¹⁸ ce qui correspond à 1 eth. +**REMARQUE :** ce résultat est en wei et non en eth. Le wei est utilisé comme la plus petite dénomination d'ether. La conversion de wei vers eth est : 1 eth = 10¹⁸ wei. Donc si on convertit 0xde0b6b3a7640000 en nombre décimal, nous obtenons 1\*10¹⁸ ce qui correspond à 1 eth. -Ouf ! Notre faux argent est bien là ! +Ouf ! Notre faux argent est bien là ! ## Connecter MetaMask à votre interface utilisateur {#connect-metamask-to-your-UI} Maintenant que notre portefeuille MetaMask est configuré, connectons-y notre dApp ! -Pour respecter le paradigme [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) , nous allons créer un fichier séparé qui contient nos fonctions pour gérer la logique, les données, et les règles de notre dApp, puis passer ces fonctions à notre interface (notre composant Minter.js). +Parce que nous voulons adhérer au paradigme [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), nous allons créer un fichier séparé qui contient nos fonctions pour gérer la logique, les données et les règles de notre dapp, puis transmettre ces fonctions à notre frontend (notre composant Minter.js). ### La fonction `connectWallet` {#connect-wallet-function} -Pour cela, créons un nouveau dossier appelé `utils` dans votre dossier `src` et ajoutons-y un fichier appelé `interact.js`, qui contiendra toutes les fonctions de notre portefeuille et les interactions avec le contrat intelligent. +Pour ce faire, créons un nouveau dossier appelé `utils` dans votre répertoire `src` et ajoutons-y un fichier appelé `interact.js`, qui contiendra toutes nos fonctions d'interaction avec le portefeuille et le contrat intelligent. -Dans notre fichier `interact.js`, nous écrirons une fonction `connectWallet`, que nous importerons et appellerons dans notre composant `Minter.js`. +Dans notre fichier `interact.js`, nous allons écrire une fonction `connectWallet`, que nous importerons et appellerons ensuite dans notre composant `Minter.js`. -Ajoutez ce qui suit dans le fichier `interact.js` +Dans votre fichier `interact.js`, ajoutez ce qui suit ```javascript export const connectWallet = async () => { @@ -243,7 +245,7 @@ export const connectWallet = async () => { method: "eth_requestAccounts", }) const obj = { - status: "👆🏽 Write a message in the text-field above.", + status: "👆🏽 Écrivez un message dans le champ de texte ci-dessus.", address: addressArray[0], } return obj @@ -261,8 +263,8 @@ export const connectWallet = async () => {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your - browser. + Vous devez installer MetaMask, un portefeuille Ethereum virtuel, dans votre + navigateur.

@@ -274,26 +276,26 @@ export const connectWallet = async () => { Décomposons ce que fait ce code : -Premièrement, notre fonction vérifie si `window.ethereum` est activé dans votre navigateur. +Tout d'abord, notre fonction vérifie si `window.ethereum` est activé dans votre navigateur. -`window.ethereum` est une API globale injectée par MetaMask et d'autres fournisseurs de portefeuille qui permet aux sites web de faire des requêtes vers les comptes Ethereum des utilisateurs. S'il est approuvé, un site peut lire les données des blockchains auxquels l'utilisateur est connecté et proposer à l'utilisateur de signer des messages et des transactions. Consultez la [documentation MetaMask](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents) pour plus d'infos ! +`window.ethereum` est une API globale injectée par MetaMask et d'autres fournisseurs de portefeuilles qui permet aux sites Web de demander les comptes Ethereum des utilisateurs. S'il est approuvé, un site peut lire les données des blockchains auxquels l'utilisateur est connecté et proposer à l'utilisateur de signer des messages et des transactions. Consultez la [documentation de MetaMask](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents) pour plus d'informations ! -Si `window.ethereum` _n'est pas_ présent, alors cela signifie que Metamask n'est pas installé. Cela se traduit par un objet JSON retourné, où l'attribut `adresse` retourné est une chaîne vide, et le `status` de l'objet JSX indique que l'utilisateur doit installer MetaMask. +Si `window.ethereum` n'_est pas_ présent, cela signifie que MetaMask n'est pas installé. Cela se traduit par le renvoi d'un objet JSON, où l'`address` renvoyée est une chaîne vide, et où l'objet `status` JSX relaie que l'utilisateur doit installer MetaMask. -**La plupart des fonctions que nous écrivons retourneront des objets JSON que nous pouvons utiliser pour mettre à jour nos variables d'état et notre interface utilisateur.** +**La plupart des fonctions que nous écrivons renverront des objets JSON que nous pourrons utiliser pour mettre à jour nos variables d'état et notre interface utilisateur.** -Maintenant, si `window.ethereum` _est présent_, alors c'est là que les choses deviennent intéressantes. +Maintenant, si `window.ethereum` _est_ présent, c'est là que les choses deviennent intéressantes. -En utilisant une boucle try/catch, nous allons essayer de nous connecter à MetaMask en appelant`[window.ethereum.request({ method: "eth_requestAccounts" });](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts)`. L'appel de cette fonction ouvrira MetaMask dans le navigateur, où l'utilisateur sera invité à connecter son portefeuille à votre dApp. +À l'aide d'une boucle try/catch, nous allons essayer de nous connecter à MetaMask en appelant [`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts). L'appel de cette fonction ouvrira MetaMask dans le navigateur, où l'utilisateur sera invité à connecter son portefeuille à votre dApp. -- Si l'utilisateur choisit de se connecter, `method : "eth_requestAccounts"` retournera une table qui contient toutes les adresses du compte de l'utilisateur qui sont connectées à la dApp. Au final, notre fonction `connectWallet` retourne un objet JSON qui contient la _première_ `address` dans cette table \(voir ligne 9\\) et un message `status` qui invite l'utilisateur à écrire un message sur le contrat intelligent. -- Si l'utilisateur rejette la connexion, alors l'objet JSON contiendra une chaîne vide pour l'`address` retournée et un message `status` qui indique que l'utilisateur a rejeté la connexion. +- Si l'utilisateur choisit de se connecter, `method: "eth_requestAccounts"` renverra un tableau contenant toutes les adresses de compte de l'utilisateur qui sont connectées à la dapp. Au total, notre fonction `connectWallet` renverra un objet JSON qui contient la _première_ `adresse` de ce tableau (voir ligne 9) et un message `d'état` qui invite l'utilisateur à écrire un message au contrat intelligent. +- Si l'utilisateur rejette la connexion, l'objet JSON contiendra une chaîne vide pour l'`address` renvoyée et un message `d'état` indiquant que l'utilisateur a rejeté la connexion. -### Ajouter une fonction connectWallet à votre composant Minter.js {#add-connect-wallet} +### Ajouter la fonction connectWallet à votre composant d'interface utilisateur Minter.js {#add-connect-wallet} -Maintenant que nous avons écrit cette fonction `connectWallet`, connectons-la à notre composant `Minter.js.`. +Maintenant que nous avons écrit cette fonction `connectWallet`, connectons-la à notre composant `Minter.js`. -Tout d'abord, nous allons devoir importer notre fonction dans notre fichier `Minter.js` en ajoutant `import { connectWallet } from "./utils/interact.js";` en haut du fichier `Minter.js`. Les 11 premières lignes de votre `Minter.js` devraient maintenant ressembler à ceci : +Tout d'abord, nous devrons importer notre fonction dans notre fichier `Minter.js` en ajoutant `import { connectWallet } from "./utils/interact.js";` au début du fichier `Minter.js`. Vos 11 premières lignes de `Minter.js` devraient maintenant ressembler à ceci : ```javascript import { useEffect, useState } from "react"; @@ -301,7 +303,7 @@ import { connectWallet } from "./utils/interact.js"; const Minter = (props) => { - //State variables + //Variables d'état const [walletAddress, setWallet] = useState(""); const [status, setStatus] = useState(""); const [name, setName] = useState(""); @@ -309,7 +311,7 @@ const Minter = (props) => { const [url, setURL] = useState(""); ``` -Puis, dans notre fonction `connectWalletPressed`, nous appellerons notre fonction importée `connectWallet`, comme suit : +Ensuite, à l'intérieur de notre fonction `connectWalletPressed`, nous appellerons notre fonction `connectWallet` importée, comme ceci : ```javascript const connectWalletPressed = async () => { @@ -319,11 +321,11 @@ const connectWalletPressed = async () => { } ``` -Vous avez remarqué comment la plupart de nos fonctionnalités sont sorties de notre composant `Minter.js` depuis le fichier `interact.js` ? C'est ainsi que nous respectons le paradigme M-V-C ! +Remarquez-vous que la plupart de nos fonctionnalités sont extraites de notre composant `Minter.js` à partir du fichier `interact.js` ? C'est ainsi que nous respectons le paradigme M-V-C ! -Dans `connectWalletPressed`, nous faisons simplement un appel await à notre fonction importée `connectWallet`, et en utilisant sa réponse, nous mettons à jour nos variables `status` et `walletAddress` via leurs hooks d'états. +Dans `connectWalletPressed`, nous effectuons simplement un appel en attente vers notre fonction importée `connectWallet` et, à l'aide de sa réponse, nous mettons à jour nos variables `status` et `walletAddress` via leurs hooks d'état. -Maintenant, enregistrons à la fois les fichiers `Minter.js` et `interact.js` et testons notre interface utilisateur. +Maintenant, enregistrons les deux fichiers `Minter.js` et `interact.js` et testons notre interface utilisateur jusqu'à présent. Ouvrez votre navigateur sur localhost:3000, et cliquez sur le bouton « Connect Wallet » en haut à droite de la page. @@ -333,11 +335,11 @@ Vous devriez voir que le bouton du portefeuille précise maintenant que votre ad Ensuite, essayez de rafraîchir la page... c'est étrange. Notre bouton de portefeuille nous invite à connecter MetaMask bien qu'il soit déjà connecté... -Mais ne vous inquiétez pas ! Nous pouvons facilement corriger cela en implémentant une fonction appelée `getCurrentWalletConnected`, qui vérifiera si une adresse est déjà connectée à notre dApp, et mettra à jour notre interface en conséquence ! +Mais ne vous inquiétez pas ! Nous pouvons facilement résoudre ce problème en implémentant une fonction appelée `getCurrentWalletConnected`, qui vérifiera si une adresse est déjà connectée à notre dapp et mettra à jour notre interface utilisateur en conséquence ! -### La fonction `getCurrentWalletConnected` {#get-current-wallet} +### La fonction getCurrentWalletConnected {#get-current-wallet} -Dans votre fichier `interact.js`, ajoutez la fonction suivante `getCurrentWalletted` : +Dans votre fichier `interact.js`, ajoutez la fonction `getCurrentWalletConnected` suivante : ```javascript export const getCurrentWalletConnected = async () => { @@ -349,12 +351,12 @@ export const getCurrentWalletConnected = async () => { if (addressArray.length > 0) { return { address: addressArray[0], - status: "👆🏽 Write a message in the text-field above.", + status: "👆🏽 Écrivez un message dans le champ de texte ci-dessus.", } } else { return { address: "", - status: "🦊 Connect to MetaMask using the top right button.", + status: "🦊 Connectez-vous à MetaMask en utilisant le bouton en haut à droite.", } } } catch (err) { @@ -371,8 +373,8 @@ export const getCurrentWalletConnected = async () => {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your - browser. + Vous devez installer MetaMask, un portefeuille Ethereum virtuel, dans votre + navigateur.

@@ -382,9 +384,9 @@ export const getCurrentWalletConnected = async () => { } ``` -Ce code est _très_ similaire à la fonction `connectWallet` que nous venons d'écrire plus tôt. +Ce code est _très_ similaire à la fonction `connectWallet` que nous venons d'écrire. -La différence principale est qu'au lieu d'appeler la méthode `eth_requestAccounts`, qui ouvre MetaMask pour que l'utilisateur puisse connecter son portefeuille, ici nous appelons la méthode `eth_accounts`, qui renvoie simplement un tableau contenant les adresses MetaMask actuellement connectées à notre dApp. +La principale différence est qu'au lieu d'appeler la méthode `eth_requestAccounts`, qui ouvre MetaMask pour que l'utilisateur connecte son portefeuille, nous appelons ici la méthode `eth_accounts`, qui renvoie simplement un tableau contenant les adresses MetaMask actuellement connectées à notre dapp. Pour voir cette fonction en action, appelons-la dans la fonction `useEffect` de notre composant `Minter.js`. @@ -394,7 +396,7 @@ Comme nous l'avons fait pour `connectWallet`, nous devons importer cette fonctio import { useEffect, useState } from "react" import { connectWallet, - getCurrentWalletConnected, //import here + getCurrentWalletConnected, //importer ici } from "./utils/interact.js" ``` @@ -408,11 +410,11 @@ useEffect(async () => { }, []) ``` -Remarquez que nous utilisons la réponse de notre appel à `getCurrentWalletConnected` pour mettre à jour nos variables d'état `walletAddress` et `status`. +Notez que nous utilisons la réponse de notre appel à `getCurrentWalletConnected` pour mettre à jour nos variables d'état `walletAddress` et `status`. Une fois que vous avez ajouté ce code, essayez de rafraîchir votre fenêtre de navigateur. Le bouton devrait indiquer que vous êtes connecté et afficher un aperçu de l'adresse de votre portefeuille connecté, même après avoir été actualisé ! -### Implémenter `addWalletListener` {#implement-add-wallet-listener} +### Implémenter addWalletListener {#implement-add-wallet-listener} La dernière étape de la configuration de notre dApp de portefeuille consiste à mettre en place le listener de portefeuille afin que notre interface utilisateur soit mise à jour lorsque l'état de notre portefeuille change, par exemple lorsque l'utilisateur se déconnecte ou change de compte. @@ -424,10 +426,10 @@ function addWalletListener() { window.ethereum.on("accountsChanged", (accounts) => { if (accounts.length > 0) { setWallet(accounts[0]) - setStatus("👆🏽 Write a message in the text-field above.") + setStatus("👆🏽 Écrivez un message dans le champ de texte ci-dessus.") } else { setWallet("") - setStatus("🦊 Connect to MetaMask using the top right button.") + setStatus("🦊 Connectez-vous à MetaMask en utilisant le bouton en haut à droite.") } }) } else { @@ -435,7 +437,7 @@ function addWalletListener() {

{" "} 🦊 - You must install MetaMask, a virtual Ethereum wallet, in your browser. + Vous devez installer MetaMask, un portefeuille Ethereum virtuel, dans votre navigateur.

) @@ -445,9 +447,9 @@ function addWalletListener() { Décomposons rapidement ce qui se passe ici : -- Premièrement, notre fonction vérifie si `window.ethereum` est activé \(ex. : MetaMask est installé\). - - Si ce n'est pas le cas, nous fixons simplement notre variable d'état `status` à une chaîne de caractères JSX qui invite l'utilisateur à installer MetaMask. - - S'il est activé, nous configurons le listener `window.ethereum.on("accountsChanged")` à la ligne 3 qui écoute les changements d'état dans le portefeuille MetaMask, qui les incluent lorsque l'utilisateur connecte un compte additionnel à la dApp, change de compte ou déconnecte un compte. S'il existe au moins un compte connecté, la variable d'état `walletAddress` est mise à jour comme premier compte dans le tableau des comptes `accounts` retourné par l'écouteur. Sinon, `walletAdresse` est défini comme une chaîne de caractères vide. +- Tout d'abord, notre fonction vérifie si `window.ethereum` est activé (c'est-à-dire si MetaMask est installé). + - Si ce n'est pas le cas, nous définissons simplement notre variable d'état `status` sur une chaîne JSX qui invite l'utilisateur à installer MetaMask. + - S'il est activé, nous configurons l'écouteur `window.ethereum.on("accountsChanged")` à la ligne 3 qui écoute les changements d'état dans le portefeuille MetaMask, qui incluent le moment où l'utilisateur connecte un compte supplémentaire à la dapp, change de compte ou déconnecte un compte. S'il y a au moins un compte connecté, la variable d'état `walletAddress` est mise à jour en tant que premier compte dans le tableau `accounts` renvoyé par l'écouteur. Sinon, `walletAddress` est défini comme une chaîne vide. Enfin, nous devons l'appeler dans notre fonction `useEffect` : @@ -463,11 +465,11 @@ useEffect(async () => { Et voilà ! Nous avons terminé la programmation de toutes les fonctionnalités de notre portefeuille ! Maintenant que notre portefeuille est configuré, regardons comment créer notre NFT ! -## Métadonnées NFT 101 {#nft-metadata-101} +## NFT Metadonnées 101 {#nft-metadata-101} Rappelez-vous que les métadonnées NFT dont nous venons de parler à l'étape 0 de ce tutoriel, donnent vie à un NFT, lui permettant d'avoir des propriétés, comme un actif numérique, un nom, une description et d'autres attributs. -Nous allons devoir configurer ces métadonnées sous forme d'objet JSON et les stocker, afin de pouvoir les transmettre en tant que paramètre `tokenURI` lors de l'appel de la fonction `mintNFT` de notre contrat intelligent. +Nous allons devoir configurer ces métadonnées en tant qu'objet JSON et les stocker, afin de pouvoir les transmettre en tant que paramètre `tokenURI` lors de l'appel de la fonction `mintNFT` de notre contrat intelligent. Le texte des champs « Lien vers l'actif », « Nom » et « Description » comprendra les différentes propriétés des métadonnées de notre NFT. Nous allons formater ces métadonnées sous la forme d'un objet JSON, mais il existe plusieurs options pour le stockage de cet objet JSON : @@ -475,39 +477,39 @@ Le texte des champs « Lien vers l'actif », « Nom » et « Description » comp - Nous pourrions le stocker sur un serveur centralisé, comme AWS ou Firebase. Mais cela irait à l'encontre de notre philosophie de décentralisation. - Nous pourrions utiliser IPFS, un protocole décentralisé et un réseau peer-to-peer pour stocker et partager des données dans un système de fichiers distribué. Comme ce protocole est décentralisé et gratuit, c'est notre meilleure option ! -Pour stocker nos métadonnées sur IPFS, nous allons utiliser [Pinata](https://pinata.cloud/), une API et une boîte à outils IPFS très pratique. Dans l'étape suivante, nous vous expliquerons exactement comment faire ! +Pour stocker nos métadonnées sur IPFS, nous utiliserons [Pinata](https://pinata.cloud/), une API et une boîte à outils IPFS pratiques. Dans l'étape suivante, nous vous expliquerons exactement comment faire ! ## Utiliser Pinata pour épingler vos métadonnées sur IPFS {#use-pinata-to-pin-your-metadata-to-IPFS} -Si vous n'avez pas de compte [Pinata](https://pinata.cloud/), créez-vous un compte gratuit [ici](https://app.pinata.cloud/auth/signup) et suivez les étapes pour vérifier votre mail et votre compte. +Si vous n'avez pas de compte [Pinata](https://pinata.cloud/), créez un compte gratuit [ici](https://app.pinata.cloud/auth/signup) et suivez les étapes pour vérifier votre e-mail et votre compte. -### Créer votre clé API Pinata {#create-pinata-api-key} +### Créez votre clé API Pinata {#create-pinata-api-key} -Naviguez vers la page [https://pinata.cloud/keys](https://pinata.cloud/keys), puis sélectionnez le bouton « New Key » en haut, activez le widget Admin et nommez votre clé. +Accédez à la page [https://pinata.cloud/keys](https://pinata.cloud/keys), puis sélectionnez le bouton « Nouvelle clé » en haut, activez le widget Admin et nommez votre clé. Vous verrez ensuite une popup avec vos infos d'API. Assurez-vous de mettre cela dans un endroit sûr. Maintenant que notre clé est configurée, ajoutons-la à notre projet pour que nous puissions l'utiliser. -### Créer un fichier `.env` {#create-a-env} +### Créer un fichier .env {#create-a-env} -Nous pouvons stocker en toute sécurité notre clé et notre secret Pinata dans un fichier d'environnement. Installons le paquet [dotenv](https://www.npmjs.com/package/dotenv) dans le répertoire de votre projet. +Nous pouvons stocker en toute sécurité notre clé et notre secret Pinata dans un fichier d'environnement. Installons le [paquetage dotenv](https://www.npmjs.com/package/dotenv) dans le répertoire de votre projet. -Ouvrez un nouvel onglet dans votre terminal \(distinct de celui qui exécute l'hôte local\) et assurez-vous que vous êtes dans le dossier `starter`, puis exécutez la commande suivante dans votre terminal : +Ouvrez un nouvel onglet dans votre terminal (distinct de celui qui exécute l'hôte local) et assurez-vous que vous êtes dans le dossier `minter-starter-files`, puis exécutez la commande suivante dans votre terminal : ```text npm install dotenv --save ``` -Ensuite, créez un fichier `.env` dans le répertoire racine de vos `minter-starter-files` en entrant ce qui suit sur votre ligne de commande : +Ensuite, créez un fichier `.env` dans le répertoire racine de votre `minter-starter-files` en saisissant ce qui suit sur votre ligne de commande : ```javascript vim.env ``` -Ceci ouvrira votre fichier `.env` dans vim (un éditeur de texte). Pour l'enregistrer, appuyez sur « esc » + « : » + « q » sur votre clavier et dans cet ordre. +Cela ouvrira votre fichier `.env` dans vim (un éditeur de texte). Pour l'enregistrer, appuyez sur « esc » + « : » + « q » sur votre clavier et dans cet ordre. -Ensuite, dans VSCode, accédez à votre fichier `.env` et ajoutez votre clé API Pinata et votre secret API Secret, comme ceci : +Ensuite, dans VSCode, accédez à votre fichier `.env` et ajoutez-y votre clé API et votre secret API Pinata, comme ceci : ```text REACT_APP_PINATA_KEY = @@ -518,9 +520,9 @@ Enregistrez le fichier et vous êtes prêt à commencer à écrire la fonction p ### Implémenter pinJSONToIPFS {#pin-json-to-ipfs} -Heureusement pour nous, Pinata a une [API spécifique pour télécharger des données JSON sur IPFS](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json) et un JavaScript pratique avec un exemple d'axios que nous pouvons utiliser en opérant juste quelques petites modifications. +Heureusement pour nous, Pinata dispose d'une [API spécialement conçue pour téléverser des données JSON vers IPFS](https://docs.pinata.cloud/api-reference/endpoint/ipfs/pin-json-to-ipfs#pin-json) et d'un exemple pratique JavaScript avec axios que nous pouvons utiliser, avec quelques légères modifications. -Dans votre dossier `utils`, créons un autre fichier appelé `pinata.js` puis importez notre clé secrète Pinata à partir du fichier `.env` comme suit : +Dans votre dossier `utils`, créons un autre fichier appelé `pinata.js`, puis importons notre secret et notre clé Pinata depuis le fichier .env comme ceci : ```javascript require("dotenv").config() @@ -539,7 +541,7 @@ const axios = require("axios") export const pinJSONToIPFS = async (JSONBody) => { const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS` - //making axios POST request to Pinata ⬇️ + //faire une requête POST axios à Pinata ⬇️ return axios .post(url, JSONBody, { headers: { @@ -566,32 +568,32 @@ export const pinJSONToIPFS = async (JSONBody) => { Alors, que fait ce code exactement ? -Tout d'abord, il importe [axios](https://www.npmjs.com/package/axios), un client basé HTTP en devenir pour le navigateur et le `node.js`, que nous utiliserons pour réaliser une requête à Pinata. +Tout d'abord, il importe [axios](https://www.npmjs.com/package/axios), un client HTTP basé sur les promesses pour le navigateur et node.js, que nous utiliserons pour faire une requête à Pinata. -Ensuite, nous avons notre fonction asynchrone `pinJSONToIPFS`, qui prend un `JSONBody` en entrée et la clé API Pinata et secret dans son en-tête, pour faire une requête POST à leur API `pinJSONToIPFS`. +Ensuite, nous avons notre fonction asynchrone `pinJSONToIPFS`, qui prend un `JSONBody` en entrée et la clé et le secret de l'API Pinata dans son en-tête, le tout pour effectuer une requête POST à leur API `pinJSONToIPFS`. -- Si cette requête POST réussie, alors notre fonction retourne un objet JSON avec le booléen `success` comme `true` et la `pinataUrl` où nos métadonnées ont été épinglées. Nous utiliserons cette `pinataUrl` retournée comme entrée `tokenURI` de la fonction de `mint` de notre contrat intelligent. -- Si cette requête POST échoue, alors notre fonction retourne un objet JSON avec le booléen `success` comme `false` et une chaîne de caractères `message` qui relaie notre erreur. +- Si cette requête POST réussit, notre fonction renvoie un objet JSON avec le booléen `success` à « true » et l'`pinataUrl` où nos métadonnées ont été épinglées. Nous utiliserons cette `pinataUrl` renvoyée comme entrée `tokenURI` pour la fonction de frappe de notre contrat intelligent. +- Si cette requête POST échoue, notre fonction renvoie un objet JSON avec le booléen `success` à « false » et une chaîne `message` qui relaie notre erreur. -Comme avec notre fonction de types retournés `connectWallet`, nous retournons des objets JSON afin que nous puissions utiliser leurs paramètres pour mettre à jour nos variables d'état et notre interface utilisateur. +Comme pour les types de retour de notre fonction `connectWallet`, nous renvoyons des objets JSON afin de pouvoir utiliser leurs paramètres pour mettre à jour nos variables d'état et notre interface utilisateur. ## Charger votre contrat intelligent {#load-your-smart-contract} -Maintenant que nous avons un moyen d'envoyer nos métadonnées NFT vers IPFS via notre fonction `pinJSONToIPFS`, nous allons avoir besoin d'un moyen de charger une instance de notre contrat intelligent afin que nous puissions appeler sa fonction `mintNFT`. +Maintenant que nous avons un moyen de téléverser nos métadonnées NFT sur IPFS via notre fonction `pinJSONToIPFS`, nous allons avoir besoin d'un moyen de charger une instance de notre contrat intelligent afin de pouvoir appeler sa fonction `mintNFT`. -Comme nous l'avons mentionné précédemment dans ce tutoriel, nous utiliserons [ce contrat intelligent NFT existant](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE). Cependant, si vous souhaitez apprendre comment nous avons fait, en faire un par vous-même, nous vous recommandons vivement de consulter notre autre tutoriel, ["Comment créer un NFT](https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft). +Comme nous l'avons mentionné précédemment, dans ce tutoriel, nous utiliserons [ce contrat intelligent de NFT existant](https://ropsten.etherscan.io/address/0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE) ; cependant, si vous souhaitez apprendre comment nous l'avons créé, ou en créer un vous-même, nous vous recommandons vivement de consulter notre autre tutoriel, ["Comment créer un NFT."](https://www.alchemy.com/docs/how-to-create-an-nft). -### Le contrat ABI {#contract-abi} +### L'ABI du contrat {#contract-abi} -Si vous avez examiné en détail nos fichiers, vous aurez remarqué que dans notre répertoire `src`, il existe un fichier `contract-abi.json`. Une ABI est nécessaire pour spécifier quelle fonction un contrat invoquera en s'assurant également que la fonction retournera des données dans le format que vous attendez. +Si vous avez examiné attentivement nos fichiers, vous aurez remarqué que dans notre répertoire `src`, il y a un fichier `contract-abi.json`. Une ABI est nécessaire pour spécifier quelle fonction un contrat invoquera en s'assurant également que la fonction retournera des données dans le format que vous attendez. Nous allons également avoir besoin d'une clé API Alchemy et de l'API Alchemy Web3 pour nous connecter à la blockchain Ethereum et charger notre contrat intelligent. -### Créer votre clé API Alchemy {#create-alchemy-api} +### Créez votre clé API Alchemy {#create-alchemy-api} -Si vous n'avez pas déjà un compte Alchemy, vous pouvez [vous inscrire gratuitement ici](https://alchemy.com/?a=eth-org-nft-minter). +Si vous n'avez pas encore de compte Alchemy, [inscrivez-vous gratuitement ici.](https://alchemy.com/?a=eth-org-nft-minter) -Une fois votre compte Alchemy créé, vous pouvez générer une clé API en créant une application. Cela nous permettra de réaliser des requêtes sur le réseau de test Ropsten. +Une fois votre compte Alchemy créé, vous pouvez générer une clé API en créant une application. Cela va nous permettre d'émettre des requêtes sur le réseau de test Ropsten. Accédez à la page "Create App" dans votre Tableau de bord Alchemy, en survolant "Apps" dans la barre de navigation et en cliquant sur "Create App". @@ -601,7 +603,7 @@ Cliquez sur « Create app », et voilà ! Votre application devrait apparaître Génial ! Maintenant que nous avons créé notre URL pour l'API HTTP Alchemy, copiez-la dans votre presse-papiers... -…puis ajoutons-la à notre fichier `.env`. Dans l'ensemble, votre fichier `.env` devrait ressembler à ceci : +…et ensuite ajoutons-la à notre fichier `.env`. Dans l'ensemble, votre fichier .env devrait ressembler à ceci : ```text REACT_APP_PINATA_KEY = @@ -609,18 +611,18 @@ REACT_APP_PINATA_SECRET = REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/ ``` -Maintenant que nous avons notre contrat ABI et notre clé API Alchemy, nous sommes prêts à charger notre contrat intelligent en utilisant [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3). +Maintenant que nous avons notre ABI de contrat et notre clé d'API Alchemy, nous sommes prêts à charger notre contrat intelligent en utilisant [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3). -### Configurer votre point de terminaison Alchemy Web3 et votre contrat {#setup-alchemy-endpoint} +### Configurer votre point de terminaison et votre contrat Alchemy Web3 {#setup-alchemy-endpoint} -Tout d'abord, si vous ne l'avez pas déjà fait, vous devrez installer [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) en naviguant dans le répertoire principal : `nft-minter-tutorial` dans le terminal : +Tout d'abord, si vous ne l'avez pas déjà, vous devrez installer [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) en naviguant vers le répertoire de base : `nft-minter-tutorial` dans le terminal : ```text cd .. npm install @alch/alchemy-web3 ``` -Ensuite, revenons à notre fichier `interact.js`. En haut du fichier, ajoutez le code suivant pour importer votre clé Alchemy à partir de votre fichier `.env` et configurez votre point de terminaison Alchemy Web3 : +Revenons maintenant à notre fichier `interact.js`. En haut du fichier, ajoutez le code suivant pour importer votre clé Alchemy à partir de votre fichier .env et configurez votre point de terminaison Alchemy Web3 : ```javascript require("dotenv").config() @@ -629,7 +631,7 @@ const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3(alchemyKey) ``` -[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) est un wrapper autour de [Web3.js](https://docs.web3js.org/), fournissant des méthodes API améliorées et d'autres avantages pour faciliter votre vie en tant que développeur Web3. Il est conçu pour nécessiter une configuration minimale afin que vous puissiez commencer à l'utiliser immédiatement dans votre application ! +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) est un wrapper autour de [Web3.js](https://docs.web3js.org/), qui fournit des méthodes API améliorées et d'autres avantages cruciaux pour vous faciliter la vie en tant que développeur web3. Il est conçu pour nécessiter une configuration minimale afin que vous puissiez commencer à l'utiliser immédiatement dans votre application ! Ensuite, ajoutons notre contrat ABI et l'adresse de notre contrat à notre fichier. @@ -647,99 +649,99 @@ Une fois que nous avons les deux, nous sommes prêts à commencer à coder notre ## Implémenter la fonction mintNFT {#implement-the-mintnft-function} -Dans votre fichier `interact.js`, définissons notre fonction, `mintNFT`, qui comme son nom l'indique va frapper notre NFT. +À l'intérieur de votre fichier `interact.js`, définissons notre fonction, `mintNFT`, qui, comme son nom l'indique, frappera notre NFT. Parce que nous allons réaliser de nombreux appels asynchrones (à Pinata pour épingler nos métadonnées à IPFS, Alchemy Web3 pour charger notre contrat intelligent, et MetaMask pour signer nos transactions), notre fonction sera également asynchrone. -Les trois entrées de notre fonction seront l'`url` de notre actif numérique, `name`, et `description`. Ajoutez la signature de fonction suivante sous la fonction `connectWallet` : +Les trois entrées de notre fonction seront l'`url` de notre actif numérique, le `name` et la `description`. Ajoutez la signature de fonction suivante sous la fonction `connectWallet` : ```javascript export const mintNFT = async (url, name, description) => {} ``` -### Gestion des erreurs d'entrée {#input-error-handling} +### Gestion des erreurs de saisie {#input-error-handling} Naturellement, il est logique d'avoir une sorte de gestion des erreurs d'entrée au début de la fonction et ainsi, nous quitterons cette fonction si nos paramètres d'entrée ne sont pas corrects. Dans notre fonction, ajoutons le code suivant : ```javascript export const mintNFT = async (url, name, description) => { - //error handling + //gestion des erreurs if (url.trim() == "" || name.trim() == "" || description.trim() == "") { return { success: false, - status: "❗Please make sure all fields are completed before minting.", + status: "❗Veuillez vous assurer que tous les champs sont remplis avant la frappe.", } } } ``` -Si l'un des paramètres d'entrée est une chaîne de caractères vide, alors nous retournons un objet JSON où le booléen `succes` est `false`, et les relais de chaîne de caractère `status` signalent que tous les champs de notre interface utilisateur doivent être complétés. +Essentiellement, si l'un des paramètres d'entrée est une chaîne vide, nous renvoyons un objet JSON où le booléen `success` est à « false », et la chaîne `status` relaie que tous les champs de notre interface utilisateur doivent être remplis. -### Télécharger les métadonnées sur IPFS {#upload-metadata-to-ipfs} +### Téléverser les métadonnées sur IPFS {#upload-metadata-to-ipfs} -Une fois que nous savons que nos métadonnées sont correctement formatées, la prochaine étape est de l'envelopper dans un objet JSON et de le charger sur IPFS via le `pinJSONToIPFS` que nous avons écrit ! +Une fois que nous savons que nos métadonnées sont formatées correctement, l'étape suivante consiste à les envelopper dans un objet JSON et à les téléverser sur IPFS via le `pinJSONToIPFS` que nous avons écrit ! -Pour ce faire, nous devons d'abord importer la fonction `pinJSONToIPFS` dans notre fichier `interact.js`. Tout en haut de `interact.js`, ajoutons : +Pour ce faire, nous devons d'abord importer la fonction `pinJSONToIPFS` dans notre fichier `interact.js`. Tout en haut du fichier `interact.js`, ajoutons : ```javascript import { pinJSONToIPFS } from "./pinata.js" ``` -Rappelez-vous que `pinJSONToIPFS` prend dans un corps JSON. Ainsi, avant de passer un appel, nous allons devoir formater nos paramètres `url`, `name`, et `description` dans un objet JSON. +Rappelez-vous que `pinJSONToIPFS` prend un corps JSON. Ainsi, avant de l'appeler, nous allons devoir formater nos paramètres `url`, `name` et `description` dans un objet JSON. -Mettons à jour notre code pour créer un objet JSON appelé `metadata` puis appelons `pinJSONToIPFS` avec ce paramètre `metadata` : +Mettons à jour notre code pour créer un objet JSON appelé `metadata`, puis appelons `pinJSONToIPFS` avec ce paramètre `metadata` : ```javascript export const mintNFT = async (url, name, description) => { - //error handling + //gestion des erreurs if (url.trim() == "" || name.trim() == "" || description.trim() == "") { return { success: false, - status: "❗Please make sure all fields are completed before minting.", + status: "❗Veuillez vous assurer que tous les champs sont remplis avant la frappe.", } } - //make metadata + //créer les métadonnées const metadata = new Object() metadata.name = name metadata.image = url metadata.description = description - //make pinata call + //faire un appel pinata const pinataResponse = await pinJSONToIPFS(metadata) if (!pinataResponse.success) { return { success: false, - status: "😢 Something went wrong while uploading your tokenURI.", + status: "😢 Une erreur s'est produite lors du téléversement de votre tokenURI.", } } const tokenURI = pinataResponse.pinataUrl } ``` -Attention, nous stockons la réponse de notre appel à `pinJSONToIPFS(metadata)` dans l'objet `pinataResponse`. Ensuite, nous analysons cet objet pour vérifier les erreurs. +Notez que nous stockons la réponse de notre appel à `pinJSONToIPFS(metadata)` dans l'objet `pinataResponse`. Ensuite, nous analysons cet objet pour vérifier les erreurs. -S'il existe une erreur, nous retournons un objet JSON où le booléen `success` est `false` et que notre chaîne de caractères `status` nous signale que notre appel a échoué. Sinon, nous extrayons `pinataURL` de `pinataResponse` et la stockons comme notre variable `tokenURI`. +En cas d'erreur, nous renvoyons un objet JSON où le booléen `success` est à « false » et notre chaîne `status` relaie que notre appel a échoué. Sinon, nous extrayons la `pinataURL` de la `pinataResponse` et la stockons en tant que notre variable `tokenURI`. -Maintenant, il est temps de charger notre contrat intelligent en utilisant l'API Alchemy Web3 que nous avons initialisée en haut de notre fichier. Ajoutez la ligne de code suivante au bas de la fonction `mintNFT` pour définir le contrat sur la variable globale `window.contract` : +Maintenant, il est temps de charger notre contrat intelligent en utilisant l'API Alchemy Web3 que nous avons initialisée en haut de notre fichier. Ajoutez la ligne de code suivante au bas de la fonction `mintNFT` pour définir le contrat dans la variable globale `window.contract` : ```javascript window.contract = await new web3.eth.Contract(contractABI, contractAddress) ``` -La dernière chose à ajouter à notre fonction `mintNFT` est notre transaction Ethereum : +La dernière chose à ajouter dans notre fonction `mintNFT` est notre transaction Ethereum : ```javascript -//set up your Ethereum transaction +//configurer votre transaction Ethereum const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: window.ethereum.selectedAddress, // must match user's active address. + to: contractAddress, // Requis sauf lors des publications de contrats. + from: window.ethereum.selectedAddress, // doit correspondre à l'adresse active de l'utilisateur. data: window.contract.methods .mintNFT(window.ethereum.selectedAddress, tokenURI) - .encodeABI(), //make call to NFT smart contract + .encodeABI(), //faire appel au contrat intelligent NFT } -//sign the transaction via MetaMask +//signer la transaction via MetaMask try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -748,13 +750,13 @@ try { return { success: true, status: - "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + + "✅ Consultez votre transaction sur Etherscan : https://ropsten.etherscan.io/tx/" + txHash, } } catch (error) { return { success: false, - status: "😥 Something went wrong: " + error.message, + status: "😥 Quelque chose s'est mal passé : " + error.message, } } ``` @@ -762,54 +764,54 @@ try { Si vous êtes déjà familier avec les transactions Ethereum, vous remarquerez que la structure est assez similaire à ce que vous avez déjà vu. - Tout d'abord, nous configurons nos paramètres de transactions. - - `to` spécifie l'adresse du destinataire \(notre contrat intelligent) - - `from` spécifie le signataire de la transaction (l'adresse de l'utilisateur connectée à MetaMask : `window.ethereum.selectedAddress`) - - `data` contient l'appel à la méthode `mintNFT` de notre contrat intelligent , qui reçoit notre `tokenURI` et l'adresse du portefeuille de l'utilisateur, `window.ethereum.selectedAddress`, comme des entrées. -- Ensuite, nous faisons un appel en attente, `window.ethereum.request,` où nous demandons à MetaMask de signer la transaction. Remarquez que dans cette requête, nous spécifions notre méthode ETH \(`eth_SentTransaction`) et en la passant dans nos `transactionParameters`. À ce stade, MetaMask s'ouvrira dans le navigateur, et demandera à l'utilisateur de signer ou rejeter la transaction. - - Si la transaction est réussie, la fonction retournera un objet JSON où le booléen `success` sera défini comme vrai et la chaîne `status` invitera l'utilisateur à consulter Etherscan pour plus d'informations sur sa transaction. - - Si la transaction échoue, la fonction retournera un objet JSON où le booléen `success` sera défini comme faux, et la chaîne de caractères `status` renverra un message d'erreur. + - `to` spécifie l'adresse du destinataire (notre contrat intelligent) + - `from` spécifie le signataire de la transaction (l'adresse connectée de l'utilisateur à MetaMask : `window.ethereum.selectedAddress`) + - `data` contient l'appel à la méthode `mintNFT` de notre contrat intelligent, qui reçoit notre `tokenURI` et l'adresse du portefeuille de l'utilisateur, `window.ethereum.selectedAddress`, comme entrées +- Ensuite, nous effectuons un appel en attente, `window.ethereum.request`, où nous demandons à MetaMask de signer la transaction. Notez que, dans cette requête, nous spécifions notre méthode eth (`eth_SentTransaction`) et nous transmettons nos `transactionParameters`. À ce stade, MetaMask s'ouvrira dans le navigateur, et demandera à l'utilisateur de signer ou rejeter la transaction. + - Si la transaction réussit, la fonction renverra un objet JSON où le booléen `success` est à « true » et la chaîne `status` invite l'utilisateur à consulter Etherscan pour plus d'informations sur sa transaction. + - Si la transaction échoue, la fonction renverra un objet JSON où le booléen `success` est à « false », et la chaîne `status` relaie le message d'erreur. -Dans l'ensemble, notre fonction `mintNFT` devrait ressembler à ceci : +Au total, notre fonction `mintNFT` devrait ressembler à ceci : ```javascript export const mintNFT = async (url, name, description) => { - //error handling + //gestion des erreurs if (url.trim() == "" || name.trim() == "" || description.trim() == "") { return { success: false, - status: "❗Please make sure all fields are completed before minting.", + status: "❗Veuillez vous assurer que tous les champs sont remplis avant la frappe.", } } - //make metadata + //créer les métadonnées const metadata = new Object() metadata.name = name metadata.image = url metadata.description = description - //pinata pin request + //requête d'épinglage pinata const pinataResponse = await pinJSONToIPFS(metadata) if (!pinataResponse.success) { return { success: false, - status: "😢 Something went wrong while uploading your tokenURI.", + status: "😢 Une erreur s'est produite lors du téléversement de votre tokenURI.", } } const tokenURI = pinataResponse.pinataUrl - //load smart contract + //charger le contrat intelligent window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract(); - //set up your Ethereum transaction + //configurer votre transaction Ethereum const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: window.ethereum.selectedAddress, // must match user's active address. + to: contractAddress, // Requis sauf lors des publications de contrats. + from: window.ethereum.selectedAddress, // doit correspondre à l'adresse active de l'utilisateur. data: window.contract.methods .mintNFT(window.ethereum.selectedAddress, tokenURI) - .encodeABI(), //make call to NFT smart contract + .encodeABI(), //faire appel au contrat intelligent NFT } - //sign transaction via MetaMask + //signer la transaction via MetaMask try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -818,23 +820,23 @@ export const mintNFT = async (url, name, description) => { return { success: true, status: - "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + + "✅ Consultez votre transaction sur Etherscan : https://ropsten.etherscan.io/tx/" + txHash, } } catch (error) { return { success: false, - status: "😥 Something went wrong: " + error.message, + status: "😥 Quelque chose s'est mal passé : " + error.message, } } } ``` -C'est une fonction géante ! Maintenant, nous avons juste besoin de connecter notre fonction `mintNFT` à notre composant `Minter.js`... +C'est une fonction géante ! Maintenant, nous devons simplement connecter notre fonction `mintNFT` à notre composant `Minter.js`... ## Connecter mintNFT à notre frontend Minter.js {#connect-our-frontend} -Ouvrez votre fichier `Minter.js` et mettez à jour la ligne du haut `import{ connectWallet, getCurrentWalletConnected } from "./utils/interact.js";` pour devenir : +Ouvrez votre fichier `Minter.js` et mettez à jour la ligne `import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js";` en haut pour : ```javascript import { @@ -844,7 +846,7 @@ import { } from "./utils/interact.js" ``` -Enfin, implémentez la fonction `onMintPressed` pour faire attendre l'appel à votre fonction importée `mintNFT` et mettez à jour la variable d'état `status` pour refléter si notre transaction a réussi ou a échoué : +Enfin, implémentez la fonction `onMintPressed` pour faire un appel en attente vers votre fonction `mintNFT` importée et mettre à jour la variable d'état `status` pour refléter si notre transaction a réussi ou a échoué : ```javascript const onMintPressed = async () => { @@ -853,22 +855,22 @@ const onMintPressed = async () => { } ``` -## Déployer votre NFT sur un site Web en ligne {#deploy-your-NFT} +## Déployez votre NFT sur un site web en direct {#deploy-your-NFT} -Prêt à mettre en ligne votre projet pour que les utilisateurs puissent interagir avec ? Consultez [ce tutoriel](https://docs.alchemy.com/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online) pour déployer votre Minter sur un site Web directement. +Prêt à mettre en ligne votre projet pour que les utilisateurs puissent interagir avec ? Consultez [ce tutoriel](https://docs.alchemy.com/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online) pour déployer votre Minter sur un site web en direct. Encore une dernière étape... -## Prendre d'assaut le monde de la blockchain {#take-the-blockchain-world-by-storm} +## Prenez d'assaut le monde de la blockchain {#take-the-blockchain-world-by-storm} Je plaisante, vous êtes arrivé à la fin du tutoriel ! Pour récapituler, en construisant un Minter de NFT, vous avez appris avec succès à : - Vous connecter à MetaMask via votre projet en frontend -- Appeler les méthodes du contrat intelligent depuis votre interface en frontend +- Appeler les méthodes du contrat intelligent depuis votre interface - Signer des transactions à l'aide de MetaMask -Sans doute vous aimeriez pouvoir montrer les NFT mis à jour via votre dApp dans votre portefeuille — alors n'oubliez pas de consulter notre bref tutoriel : [Comment consulter votre NFT dans votre portefeuille](https://docs.alchemyapi.io/alchemy/tutorials/how-to-write-and-deploy-a-nft-smart-contract/how-to-view-your-nft-in-your-wallet) ! +Vraisemblablement, vous aimeriez pouvoir montrer les NFT frappés via votre dapp dans votre portefeuille — alors n'oubliez pas de consulter notre tutoriel rapide [Comment voir votre NFT dans votre portefeuille](https://www.alchemy.com/docs/how-to-view-your-nft-in-your-mobile-wallet) ! -Et, comme toujours, si vous avez des questions, nous sommes là pour vous aider dans le [Discord Alchemy](https://discord.gg/gWuC7zB). Nous avons hâte de voir comment vous appliquez les concepts de ce tutoriel à vos futurs projets ! +Et, comme toujours, si vous avez des questions, nous sommes là pour vous aider dans le [Discord d'Alchemy](https://discord.gg/gWuC7zB). Nous avons hâte de voir comment vous appliquez les concepts de ce tutoriel à vos futurs projets ! diff --git a/public/content/translations/fr/developers/tutorials/optimism-std-bridge-annotated-code/index.md b/public/content/translations/fr/developers/tutorials/optimism-std-bridge-annotated-code/index.md index 62c03df2159..7ca0e0decf1 100644 --- a/public/content/translations/fr/developers/tutorials/optimism-std-bridge-annotated-code/index.md +++ b/public/content/translations/fr/developers/tutorials/optimism-std-bridge-annotated-code/index.md @@ -1,84 +1,89 @@ --- -title: "Introduction au contrat de passerelle standard Optimism" -description: Comment fonctionne la passerelle standard d'Optimism ? Pourquoi fonctionne-t-elle de cette façon ? +title: "Présentation du contrat de pont standard d'Optimism" +description: "Comment fonctionne le pont standard d'Optimism ? Pourquoi fonctionne-t-il de cette manière ?" author: Ori Pomerantz -tags: - - "solidity" - - "bridge" - - "Couche 2" +tags: [ "solidité", "pont", "couche 2" ] skill: intermediate published: 2022-03-30 lang: fr --- -[Optimism](https://www.optimism.io/) est un [rollup optimiste](/developers/docs/scaling/optimistic-rollups/). Les rollups Optimistics peuvent traiter les transactions à un prix beaucoup plus bas que le réseau principal Ethereum (également connu sous le nom de couche 1 ou L1), car les transactions sont traitées uniquement par quelques nœuds en lieu et place de tous les nœuds du réseau. En même temps, les données sont toutes écrites sur L1 afin que tout puisse être prouvé et reconstruit avec toutes les garanties d'intégrité et de disponibilité du réseau principal. +[Optimism](https://www.optimism.io/) est un [rollup optimiste](/developers/docs/scaling/optimistic-rollups/). +Les rollups optimistes peuvent traiter les transactions à un prix beaucoup plus bas que le réseau principal Ethereum (également connu sous le nom de couche 1 ou L1), car les transactions sont traitées uniquement par quelques nœuds, au lieu de chaque nœud sur le réseau. +En même temps, toutes les données sont écrites sur la L1 afin que tout puisse être prouvé et reconstruit avec toutes les garanties d'intégrité et de disponibilité du réseau principal. -Pour utiliser les actifs L1 sur Optimism (ou n'importe quel autre L2), les actifs doivent être [connectés](/bridges/#prerequisites). Une façon d'y arriver est pour les utilisateurs de verrouiller les actifs (ETH et les [jetons ERC-20](/developers/docs/standards/tokens/erc-20/) sont les plus communs) sur L1 et de recevoir des actifs équivalents à utiliser sur L2. Finalement, celui qui se retrouve avec souhaitera peut-être les ramener en L1. En faisant cela, les actifs sont brûlés sur L2 puis redistribués à l'utilisateur sur L1. +Pour utiliser les actifs de L1 sur Optimism (ou toute autre L2), les actifs doivent être [pontés](/bridges/#prerequisites). +Une façon d'y parvenir est pour les utilisateurs de verrouiller des actifs (l'ETH et les [jetons ERC-20](/developers/docs/standards/tokens/erc-20/) sont les plus courants) sur la L1, et de recevoir des actifs équivalents à utiliser sur la L2. +Finalement, quiconque se retrouve avec ces actifs pourrait vouloir les ponter à nouveau vers la L1. +Ce faisant, les actifs sont brûlés sur la L2, puis restitués à l'utilisateur sur la L1. -C'est ainsi que fonctionne la [passerelle standard Optimism](https://docs.optimism.io/app-developers/bridging/standard-bridge). Dans cet article, nous passerons en revue le code source de cette passerelle pour comprendre comment elle fonctionne et l'étudier comme un exemple de code Solidity parfaitement écrit. +C'est ainsi que fonctionne le [pont standard d'Optimism](https://docs.optimism.io/app-developers/bridging/standard-bridge). +Dans cet article, nous passons en revue le code source de ce pont pour voir comment il fonctionne et l'étudier comme un exemple de code Solidity bien écrit. ## Flux de contrôle {#control-flows} -La passerelle dispose de deux flux principaux : +Le pont a deux flux principaux : -- Dépôt (de L1 vers L2) -- Retrait (de L2 vers L1) +- Dépôt (de L1 à L2) +- Retrait (de L2 à L1) ### Flux de dépôt {#deposit-flow} #### Couche 1 {#deposit-flow-layer-1} -1. En cas de dépôt d'un ERC-20, le déposant affecte à la passerelle une provision pour dépenser le montant déposé -2. Le déposant appelle la passerelle L1 (`depositERC20`, `depositERC20To`, `depositETH`, ou `depositETHTo`) -3. La passerelle L1 prend possession de l'actif connecté - - ETH : l'actif est transféré par le déposant dans le cadre de l'appel - - ERC-20 : l'actif est transféré par la passerelle à elle-même en utilisant la provision fournie par le déposant -4. La passerelle de connexion L1 utilise le mécanisme de message inter-domaine pour appeler `finalizeDeposit` sur la passerelle de connexion L2 +1. En cas de dépôt d'un ERC-20, le déposant donne au pont une autorisation de dépenser le montant déposé +2. Le déposant appelle le pont L1 (`depositERC20`, `depositERC20To`, `depositETH` ou `depositETHTo`) +3. Le pont L1 prend possession de l'actif ponté + - ETH : L'actif est transféré par le déposant dans le cadre de l'appel + - ERC-20 : L'actif est transféré par le pont à lui-même en utilisant l'autorisation fournie par le déposant +4. Le pont L1 utilise le mécanisme de message inter-domaines pour appeler `finalizeDeposit` sur le pont L2 #### Couche 2 {#deposit-flow-layer-2} -5. La passerelle de connexion L2 vérifie que l'appel `finalizeDeposit` est légitime : - - Provient du contrat de message inter-domaine - - Était à l'origine en provenance de la passerelle de connexion sur L1 -6. La passerelle de connexion L2 vérifie si le contrat de jeton ERC-20 sur L2 est le bon : - - Le contrat L2 signale que son homologue L1 est identique à celui dont les jetons provenaient sur L1 - - Le contrat L2 signale qu'il prend en charge l'interface correcte ([en utilisant ERC-165](https://eips.ethereum.org/EIPS/eip-165)). -7. Si le contrat L2 est le bon, appelez-le pour frapper le nombre approprié de jetons à l'adresse appropriée. Sinon, commencez un processus de retrait pour permettre à l'utilisateur de réclamer les jetons sur L1. +5. Le pont L2 vérifie que l'appel à `finalizeDeposit` est légitime : + - Provenant du contrat de message inter-domaines + - Était initialement du pont sur la L1 +6. Le pont L2 vérifie si le contrat de jeton ERC-20 sur la L2 est le bon : + - Le contrat L2 signale que son homologue L1 est le même que celui d'où provenaient les jetons sur la L1 + - Le contrat L2 signale qu'il prend en charge l'interface correcte ([en utilisant l'ERC-165](https://eips.ethereum.org/EIPS/eip-165)). +7. Si le contrat L2 est le bon, appelez-le pour frapper le nombre de jetons approprié à l'adresse appropriée. Sinon, démarrez un processus de retrait pour permettre à l'utilisateur de réclamer les jetons sur la L1. ### Flux de retrait {#withdrawal-flow} #### Couche 2 {#withdrawal-flow-layer-2} -1. Le retirant appelle la passerelle de connexion L2 (`withdraw` ou `withdrawTo`) -2. La passerelle de connexion L2 brûle le nombre approprié de jetons appartenant à `msg.sender` -3. La passerelle de connexion L2 utilise le mécanisme de message inter-domaine pour appeler `finalizeETHWithdrawal` ou `finalizeERC20Withdrawal` de la passerelle L1 +1. Le retireur appelle le pont L2 (`withdraw` ou `withdrawTo`) +2. Le pont L2 brûle le nombre approprié de jetons appartenant à `msg.sender` +3. Le pont L2 utilise le mécanisme de message inter-domaines pour appeler `finalizeETHWithdrawal` ou `finalizeERC20Withdrawal` sur le pont L1 #### Couche 1 {#withdrawal-flow-layer-1} -4. La passerelle de connexion L1 vérifie que l'appel à `finalizeETHWithal` ou à `finalizeERC20Withal` est légitime : - - Provient du mécanisme de message inter-domaine - - Était à l'origine en provenance de la passerelle de connexion sur L2 -5. La passerelle L1 transfère l'actif approprié (ETH ou ERC-20) à l'adresse appropriée +4. Le pont L1 vérifie que l'appel à `finalizeETHWithdrawal` ou `finalizeERC20Withdrawal` est légitime : + - Provenant du mécanisme de message inter-domaines + - Était initialement du pont sur la L2 +5. Le pont L1 transfère l'actif approprié (ETH ou ERC-20) à l'adresse appropriée ## Code de la couche 1 {#layer-1-code} -C'est le code qui s'exécute sur L1, le réseau principal Ethereum. +C'est le code qui s'exécute sur la L1, le réseau principal Ethereum. ### IL1ERC20Bridge {#IL1ERC20Bridge} -[Cette interface est définie ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1ERC20Bridge.sol). Elle comprend les fonctions et les définitions requises pour la connexion en passerelle des jetons ERC-20. +[Cette interface est définie ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1ERC20Bridge.sol). +Elle inclut les fonctions et les définitions nécessaires au pontage des jetons ERC-20. ```solidity // SPDX-License-Identifier: MIT ``` -[La plupart du code Optimism est publié sous la licence MIT](https://help.optimism.io/hc/en-us/articles/4411908707995-What-software-license-does-Optimism-use-). +[La plupart du code d'Optimism est publié sous la licence MIT](https://help.optimism.io/hc/en-us/articles/4411908707995-What-software-license-does-Optimism-use-). ```solidity pragma solidity >0.5.0 <0.9.0; ``` -Lors de l'écriture de cet article, la dernière version de Solidity était 0.8.12. Jusqu'à la publication de la version 0.9.0, nous ne saurons pas si ce code est compatible ou non. +Au moment de la rédaction, la dernière version de Solidity est la 0.8.12. +Tant que la version 0.9.0 n'est pas sortie, nous ne savons pas si ce code est compatible avec elle ou non. ```solidity /** @@ -86,20 +91,23 @@ Lors de l'écriture de cet article, la dernière version de Solidity était 0.8. */ interface IL1ERC20Bridge { /********** - * Events * + * Événements * **********/ event ERC20DepositInitiated( ``` -Dans le terminologique des passerelles pour Optimism _deposit_ signifie transférer de L1 vers L2, et _withdrawal_ signifie un transfert de L2 vers L1. +Dans la terminologie des ponts Optimism, _deposit_ (dépôt) signifie un transfert de la L1 vers la L2, et _withdrawal_ (retrait) signifie un transfert de la L2 vers la L1. ```solidity address indexed _l1Token, address indexed _l2Token, ``` -Dans la plupart des cas, l'adresse d'un ERC-20 sur L1 n'est pas la même que celle de l'équivalent ERC-20 sur L2. [Vous pouvez consulter la liste des adresses de jetons ici](https://static.optimism.io/optimism.tokenlist.json). L'adresse avec `chainId` 1 est sur L1 (le réseau principal) et l'adresse avec `chainId` 10 est sur L2 (Optimism). Les deux autres valeurs `chainId` sont pour le réseau de test Kovan (42) et le réseau de test Optimistic Kovan (69). +Dans la plupart des cas, l'adresse d'un ERC-20 sur la L1 n'est pas la même que l'adresse de l'ERC-20 équivalent sur la L2. +[Vous pouvez voir la liste des adresses de jetons ici](https://static.optimism.io/optimism.tokenlist.json). +L'adresse avec `chainId` 1 est sur la L1 (réseau principal) et l'adresse avec `chainId` 10 est sur la L2 (Optimism). +Les deux autres valeurs de `chainId` sont pour le réseau de test Kovan (42) et le réseau de test Optimistic Kovan (69). ```solidity address indexed _from, @@ -122,33 +130,35 @@ Il est possible d'ajouter des notes aux transferts, auquel cas elles sont ajout ); ``` -Le même contrat passerelle gère les transferts dans les deux sens. Dans le cas de la passerelle L1, cela signifie l'initialisation des dépôts et la finalisation des retraits. +Le même contrat de pont gère les transferts dans les deux sens. +Dans le cas du pont L1, cela signifie l'initialisation des dépôts et la finalisation des retraits. ```solidity /******************** - * Public Functions * + * Fonctions publiques * ********************/ /** - * @dev get the address of the corresponding L2 bridge contract. - * @return Address of the corresponding L2 bridge contract. + * @dev obtient l'adresse du contrat de pont L2 correspondant. + * @return Adresse du contrat de pont L2 correspondant. */ function l2TokenBridge() external returns (address); ``` -Cette fonction n'est pas vraiment nécessaire, car sur L2 c'est un contrat prédéployé, donc il sera toujours à l'adresse `0x420000000000000000000000000000000000000000000010`. Il est ici pour la symétrie avec la passerelle L2, car l'adresse de la passerelle de connexion L1 n'est _pas_ à connaître. +Cette fonction n'est pas vraiment nécessaire, car sur la L2, c'est un contrat prédéployé, donc il est toujours à l'adresse `0x4200000000000000000000000000000000000010`. +Elle est là pour la symétrie avec le pont L2, car l'adresse du pont L1 n'est _pas_ triviale à connaître. ```solidity /** - * @dev deposit an amount of the ERC20 to the caller's balance on L2. - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _amount Amount of the ERC20 to deposit - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev dépose un montant d'ERC20 sur le solde de l'appelant sur la L2. + * @param _l1Token Adresse de l'ERC20 de L1 que nous déposons + * @param _l2Token Adresse de l'ERC20 respectif de L2 + * @param _amount Montant de l'ERC20 à déposer + * @param _l2Gas Limite de gaz requise pour finaliser le dépôt sur la L2. + * @param _data Données facultatives à transmettre à la L2. Ces données sont fournies + * uniquement pour la commodité des contrats externes. En dehors de l'application d'une longueur + * maximale, ces contrats ne fournissent aucune garantie sur leur contenu. */ function depositERC20( address _l1Token, @@ -159,19 +169,21 @@ Cette fonction n'est pas vraiment nécessaire, car sur L2 c'est un contrat préd ) external; ``` -Le paramètre `_l2Gas` est le montant de gaz L2 que la transaction est autorisée à dépenser. [Jusqu'à une certaine limite (haute), c'est gratuit](https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions-2), donc à moins que le contrat ERC-20 ne fasse quelque chose de vraiment étrange lors de la frappe, il ne devrait pas y avoir de problème. Cette fonction prend en charge le scénario commun où un utilisateur relie les actifs à la même adresse sur une blockchain différente. +Le paramètre `_l2Gas` est la quantité de gaz L2 que la transaction est autorisée à dépenser. +[Jusqu'à une certaine limite (élevée), c'est gratuit](https://community.optimism.io/docs/developers/bridge/messaging/#for-l1-%E2%87%92-l2-transactions-2), donc à moins que le contrat ERC-20 ne fasse quelque chose de vraiment étrange lors de la frappe, cela ne devrait pas être un problème. +Cette fonction prend en charge le scénario courant, où un utilisateur ponte des actifs vers la même adresse sur une autre blockchain. ```solidity /** - * @dev deposit an amount of ERC20 to a recipient's balance on L2. - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _to L2 address to credit the withdrawal to. - * @param _amount Amount of the ERC20 to deposit. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev dépose un montant d'ERC20 sur le solde d'un destinataire sur la L2. + * @param _l1Token Adresse de l'ERC20 de L1 que nous déposons + * @param _l2Token Adresse de l'ERC20 respectif de L2 + * @param _to Adresse L2 sur laquelle créditer le retrait. + * @param _amount Montant de l'ERC20 à déposer. + * @param _l2Gas Limite de gaz requise pour finaliser le dépôt sur la L2. + * @param _data Données facultatives à transmettre à la L2. Ces données sont fournies + * uniquement pour la commodité des contrats externes. En dehors de l'application d'une longueur + * maximale, ces contrats ne fournissent aucune garantie sur leur contenu. */ function depositERC20To( address _l1Token, @@ -187,22 +199,22 @@ Cette fonction est presque identique à `depositERC20`, mais elle vous permet d' ```solidity /************************* - * Cross-chain Functions * + * Fonctions inter-chaînes * *************************/ /** - * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the - * L1 ERC20 token. - * This call will fail if the initialized withdrawal from L2 has not been finalized. + * @dev Finalise un retrait de la L2 vers la L1, et crédite les fonds sur le solde du destinataire du + * jeton ERC20 de L1. + * Cet appel échouera si le retrait initialisé depuis la L2 n'a pas été finalisé. * - * @param _l1Token Address of L1 token to finalizeWithdrawal for. - * @param _l2Token Address of L2 token where withdrawal was initiated. - * @param _from L2 address initiating the transfer. - * @param _to L1 address to credit the withdrawal to. - * @param _amount Amount of the ERC20 to deposit. - * @param _data Data provided by the sender on L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @param _l1Token Adresse du jeton L1 pour lequel finaliser le retrait. + * @param _l2Token Adresse du jeton L2 où le retrait a été initié. + * @param _from Adresse L2 initiant le transfert. + * @param _to Adresse L1 sur laquelle créditer le retrait. + * @param _amount Montant de l'ERC20 à déposer. + * @param _data Données fournies par l'expéditeur sur la L2. Ces données sont fournies + * uniquement pour la commodité des contrats externes. En dehors de l'application d'une longueur + * maximale, ces contrats ne fournissent aucune garantie sur leur contenu. */ function finalizeERC20Withdrawal( address _l1Token, @@ -215,16 +227,20 @@ Cette fonction est presque identique à `depositERC20`, mais elle vous permet d' } ``` -Les retraits (et autres messages de L2 vers L1) dans Optimism sont des processus en deux étapes : +Les retraits (et autres messages de la L2 à la L1) dans Optimism sont un processus en deux étapes : -1. Une transaction d'initialisation sur L2. -2. Une transaction de finalisation ou de réclamation sur L1. Cette transaction doit être réalisée après la [période de contestation des défauts](https://community.optimism.io/docs/how-optimism-works/#fault-proofs) pour que la transaction L2 se termine. +1. Une transaction d'initiation sur la L2. +2. Une transaction de finalisation ou de réclamation sur la L1. + Cette transaction doit avoir lieu après la fin de la [période de contestation des erreurs](https://community.optimism.io/docs/how-optimism-works/#fault-proofs) pour la transaction L2. ### IL1StandardBridge {#il1standardbridge} -[Cette interface est définie ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1StandardBridge.sol). Ce fichier contient des définitions d'événements et de fonctions pour ETH. Ces définitions sont très similaires à celles définies ci-dessus dans `IL1ERC20Bridge` pour ERC-20. +[Cette interface est définie ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/IL1StandardBridge.sol). +Ce fichier contient les définitions d'événements et de fonctions pour l'ETH. +Ces définitions sont très similaires à celles définies dans `IL1ERC20Bridge` ci-dessus pour l'ERC-20. -L'interface de passerelle est divisée entre deux fichiers puisque certains jetons ERC-20 nécessitent un traitement personnalisé et ne peuvent pas être traités par la passerelle de connexion standard. De cette façon, la passerelle personnalisée de connexion qui gère un tel jeton peut implémenter `IL1ERC20Bridge` et ne pas nécessiter une passerelle pour ETH. +L'interface du pont est divisée en deux fichiers car certains jetons ERC-20 nécessitent un traitement personnalisé et ne peuvent pas être gérés par le pont standard. +De cette façon, le pont personnalisé qui gère un tel jeton peut implémenter `IL1ERC20Bridge` et ne pas avoir à ponter également l'ETH. ```solidity // SPDX-License-Identifier: MIT @@ -237,7 +253,7 @@ import "./IL1ERC20Bridge.sol"; */ interface IL1StandardBridge is IL1ERC20Bridge { /********** - * Events * + * Événements * **********/ event ETHDepositInitiated( address indexed _from, @@ -247,7 +263,8 @@ interface IL1StandardBridge is IL1ERC20Bridge { ); ``` -Cet événement est presque identique à la version ERC-20 (`ERC20DepositInitiated`), mais sans les adresses de jeton L1 et L2. Il en va de même pour les autres événements et les fonctions. +Cet événement est presque identique à la version ERC-20 (`ERC20DepositInitiated`), mais sans les adresses de jeton L1 et L2. +Il en va de même pour les autres événements et les fonctions. ```solidity event ETHWithdrawalFinalized( @@ -257,11 +274,11 @@ Cet événement est presque identique à la version ERC-20 (`ERC20DepositInitiat ); /******************** - * Public Functions * + * Fonctions publiques * ********************/ /** - * @dev Deposit an amount of the ETH to the caller's balance on L2. + * @dev Dépose un montant d'ETH sur le solde de l'appelant sur la L2. . . . @@ -269,7 +286,7 @@ Cet événement est presque identique à la version ERC-20 (`ERC20DepositInitiat function depositETH(uint32 _l2Gas, bytes calldata _data) external payable; /** - * @dev Deposit an amount of ETH to a recipient's balance on L2. + * @dev Dépose un montant d'ETH sur le solde d'un destinataire sur la L2. . . . @@ -281,13 +298,13 @@ Cet événement est presque identique à la version ERC-20 (`ERC20DepositInitiat ) external payable; /************************* - * Cross-chain Functions * + * Fonctions inter-chaînes * *************************/ /** - * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the - * L1 ETH token. Since only the xDomainMessenger can call this function, it will never be called - * before the withdrawal is finalized. + * @dev Finalise un retrait de la L2 vers la L1, et crédite les fonds sur le solde du destinataire du + * jeton ETH de L1. Étant donné que seul le xDomainMessenger peut appeler cette fonction, elle ne sera jamais appelée + * avant que le retrait soit finalisé. . . . @@ -303,7 +320,7 @@ Cet événement est presque identique à la version ERC-20 (`ERC20DepositInitiat ### CrossDomainEnabled {#crossdomainenabled} -[Ce contrat](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol) a hérité des deux passerelles ([L1](#the-l1-bridge-contract) et [L2](#the-l2-bridge-contract)) pour envoyer des messages à l'autre couche. +[Ce contrat](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/CrossDomainEnabled.sol) est hérité par les deux ponts ([L1](#the-l1-bridge-contract) et [L2](#the-l2-bridge-contract)) pour envoyer des messages à l'autre couche. ```solidity // SPDX-License-Identifier: MIT @@ -313,52 +330,55 @@ pragma solidity >0.5.0 <0.9.0; import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; ``` -[Cette interface](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol) indique au contrat comment envoyer des messages à l'autre couche, en utilisant le messager inter-domaine. Cette messagerie transversale de domaine est un autre système à part entière et mériterait son propre article que j'espère écrire à l'avenir. +[Cette interface](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/bridge/ICrossDomainMessenger.sol) indique au contrat comment envoyer des messages à l'autre couche, en utilisant le messager inter-domaines. +Ce messager inter-domaines est un tout autre système, et mérite son propre article, que j'espère écrire à l'avenir. ```solidity /** * @title CrossDomainEnabled - * @dev Helper contract for contracts performing cross-domain communications + * @dev Contrat d'aide pour les contrats effectuant des communications inter-domaines * - * Compiler used: defined by inheriting contract + * Compilateur utilisé : défini par le contrat héritier */ contract CrossDomainEnabled { /************* * Variables * *************/ - // Messenger contract used to send and receive messages from the other domain. + // Contrat de messagerie utilisé pour envoyer et recevoir des messages de l'autre domaine. address public messenger; /*************** - * Constructor * + * Constructeur * ***************/ /** - * @param _messenger Address of the CrossDomainMessenger on the current layer. + * @param _messenger Adresse du CrossDomainMessenger sur la couche actuelle. */ constructor(address _messenger) { messenger = _messenger; } ``` -Le seul paramètre que le contrat a besoin de connaître est l'adresse du messager de domaines croisés sur cette couche. Ce paramètre est défini une seule fois, dans le constructeur, et ne change jamais. +Le seul paramètre que le contrat doit connaître, l'adresse du messager inter-domaines sur cette couche. +Ce paramètre est défini une fois, dans le constructeur, et ne change jamais. ```solidity /********************** - * Function Modifiers * + * Modificateurs de fonction * **********************/ /** - * Enforces that the modified function is only callable by a specific cross-domain account. - * @param _sourceDomainAccount The only account on the originating domain which is - * authenticated to call this function. + * Impose que la fonction modifiée ne puisse être appelée que par un compte inter-domaine spécifique. + * @param _sourceDomainAccount Le seul compte sur le domaine d'origine qui est + * authentifié pour appeler cette fonction. */ modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) { ``` -La messagerie inter-domaine est accessible par n'importe quel contrat sur la blockchain où elle est exécutée (soit le réseau principal Ethereum, soit Optimism). Mais nous avons besoin du pont de chaque côté pour faire confiance _uniquement_ à certains messages qui viennent de la passerelle de l'autre côté. +La messagerie inter-domaines est accessible par n'importe quel contrat sur la blockchain où elle est exécutée (soit le réseau principal Ethereum, soit Optimism). +Mais nous avons besoin que le pont de chaque côté ne fasse confiance à certains messages que s'ils proviennent du pont de l'autre côté. ```solidity require( @@ -367,7 +387,7 @@ La messagerie inter-domaine est accessible par n'importe quel contrat sur la blo ); ``` -Seuls les messages du messager inter-domaine approprié (`messenger`, comme indiqué ci-dessous) peuvent être fiables. +Seuls les messages provenant du messager inter-domaines approprié (`messenger`, comme vous le verrez ci-dessous) peuvent être fiables. ```solidity @@ -377,9 +397,10 @@ Seuls les messages du messager inter-domaine approprié (`messenger`, comme indi ); ``` -La façon dont la messagerie inter-domaine fournit l'adresse qui a envoyé un message avec l'autre couche est [la fonction `.xDomainMessageSender()`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L122-L128). Tant qu'elle est appelée dans la transaction qui a été initiée par le message, elle peut fournir ces informations. +La façon dont le messager inter-domaines fournit l'adresse qui a envoyé un message avec l'autre couche est [la fonction `.xDomainMessageSender()`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol#L122-L128). +Tant qu'elle est appelée dans la transaction qui a été initiée par le message, elle peut fournir cette information. -Nous devons nous assurer que le message que nous avons reçu provient bien de l'autre passerelle. +Nous devons nous assurer que le message que nous avons reçu provient de l'autre pont. ```solidity @@ -387,29 +408,30 @@ Nous devons nous assurer que le message que nous avons reçu provient bien de l' } /********************** - * Internal Functions * + * Fonctions internes * **********************/ /** - * Gets the messenger, usually from storage. This function is exposed in case a child contract - * needs to override. - * @return The address of the cross-domain messenger contract which should be used. + * Obtient le messager, généralement à partir du stockage. Cette fonction est exposée au cas où un contrat enfant + * aurait besoin de la remplacer. + * @return L'adresse du contrat de messager inter-domaines qui doit être utilisé. */ function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) { return ICrossDomainMessenger(messenger); } ``` -Cette fonction retourne le messager inter-domaine. Nous utilisons une fonction plutôt que la variable `messenger` pour permettre aux contrats qui héritent de celui-ci d'utiliser un algorithme pour spécifier quel messager de domaine croisé utiliser. +Cette fonction renvoie le messager inter-domaines. +Nous utilisons une fonction plutôt que la variable `messenger` pour permettre aux contrats qui en héritent d'utiliser un algorithme pour spécifier quel messager inter-domaines utiliser. ```solidity /** - * Sends a message to an account on another domain - * @param _crossDomainTarget The intended recipient on the destination domain - * @param _message The data to send to the target (usually calldata to a function with + * Envoie un message à un compte sur un autre domaine + * @param _crossDomainTarget Le destinataire prévu sur le domaine de destination + * @param _message Les données à envoyer à la cible (généralement des calldata vers une fonction avec * `onlyFromCrossDomainAccount()`) - * @param _gasLimit The gasLimit for the receipt of the message on the target domain. + * @param _gasLimit La limite de gaz pour la réception du message sur le domaine cible. */ function sendCrossDomainMessage( address _crossDomainTarget, @@ -424,10 +446,11 @@ Enfin, la fonction qui envoie un message à l'autre couche. // slither-disable-next-line reentrancy-events, reentrancy-benign ``` -[Slither](https://github.com/crytic/slither) est un analyseur statique Optimism qui fonctionne sur chaque contrat pour rechercher des vulnérabilités et d'autres problèmes potentiels. Dans notre cas, la ligne suivante déclenche deux vulnérabilités : +[Slither](https://github.com/crytic/slither) est un analyseur statique qu'Optimism exécute sur chaque contrat pour rechercher des vulnérabilités et d'autres problèmes potentiels. +Dans ce cas, la ligne suivante déclenche deux vulnérabilités : 1. [Événements de réentrance](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3) -2. [Réentrance Benign](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) +2. [Réentrance bénigne](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) ```solidity getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit); @@ -435,18 +458,19 @@ Enfin, la fonction qui envoie un message à l'autre couche. } ``` -Dans notre cas, nous ne devons pas nous inquiéter de la réentrance, nous savons que `getCrossDomainMessenger()` retourne une adresse digne de confiance, même si Slither n'a aucun moyen de le savoir. +Dans ce cas, nous ne nous inquiétons pas de la réentrance car nous savons que `getCrossDomainMessenger()` renvoie une adresse fiable, même si Slither n'a aucun moyen de le savoir. -### Le contrat de passerelle L1 {#the-l1-bridge-contract} +### Le contrat de pont L1 {#the-l1-bridge-contract} -[Le code source de ce contrat se trouve ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol). +[Le code source de ce contrat est ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol). ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; ``` -Les interfaces peuvent faire partie d'autres contrats, elles doivent donc supporter un large éventail de versions Solidity. Mais la passerelle en elle-même est notre contrat, et nous pouvons être stricts quant à la version de Solidity utilisée. +Les interfaces peuvent faire partie d'autres contrats, elles doivent donc prendre en charge une large gamme de versions de Solidity. +Mais le pont lui-même est notre contrat, et nous pouvons être stricts sur la version de Solidity qu'il utilise. ```solidity /* Interface Imports */ @@ -460,34 +484,35 @@ import { IL1ERC20Bridge } from "./IL1ERC20Bridge.sol"; import { IL2ERC20Bridge } from "../../L2/messaging/IL2ERC20Bridge.sol"; ``` -[Cette interface](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) nous permet de créer des messages pour contrôler la passerelle de connexion standard sur L2. +[Cette interface](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) nous permet de créer des messages pour contrôler le pont standard sur la L2. ```solidity import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; ``` -[Cette interface](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) nous permet de piloter les contrats ERC-20. [Vous pouvez en savoir plus sur ce sujet ici](/developers/tutorials/erc20-annotated-code/#the-interface). +[Cette interface](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) nous permet de contrôler les contrats ERC-20. +[Vous pouvez en savoir plus à ce sujet ici](/developers/tutorials/erc20-annotated-code/#the-interface). ```solidity /* Library Imports */ import { CrossDomainEnabled } from "../../libraries/bridge/CrossDomainEnabled.sol"; ``` -[Comme expliqué ci-dessus](#crossdomainenabled), ce contrat est utilisé pour la messagerie intercouche. +[Comme expliqué ci-dessus](#crossdomainenabled), ce contrat est utilisé pour la messagerie inter-couches. ```solidity import { Lib_PredeployAddresses } from "../../libraries/constants/Lib_PredeployAddresses.sol"; ``` -[`Lib_PredeployAdresses`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/constants/Lib_PredeployAddresses.sol)dispose des adresses pour les contrats L2 qui ont toujours la même adresse. Cela inclut la passerelle standard sur la L2. +`Lib_PredeployAddresses` (https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/libraries/constants/Lib_PredeployAddresses.sol) contient les adresses des contrats L2 qui ont toujours la même adresse. Cela inclut le pont standard sur la L2. ```solidity import { Address } from "@openzeppelin/contracts/utils/Address.sol"; ``` -[Utilitaires d'adresses OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol). Ils servent à distinguer les adresses contractuelles de celles appartenant à des comptes propriétaires externes (EOA). +[Utilitaires d'adresse d'OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol). Il est utilisé pour distinguer les adresses de contrat de celles appartenant à des comptes externes (EOA). -Notez que ce n'est pas une solution parfaite, car il n'y a aucun moyen de distinguer les appels directs de ceux réalisés par le constructeur d'un contrat, mais au moins cela nous permet d'identifier et de prévenir certaines erreurs utilisateurs courantes. +Notez que ce n'est pas une solution parfaite, car il n'y a aucun moyen de distinguer les appels directs des appels effectués depuis le constructeur d'un contrat, mais au moins cela nous permet d'identifier et d'éviter certaines erreurs d'utilisateur courantes. ```solidity import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -495,29 +520,29 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s [La norme ERC-20](https://eips.ethereum.org/EIPS/eip-20) prend en charge deux manières pour un contrat de signaler un échec : -1. Rétablir +1. Annuler 2. Renvoyer `false` -La gestion des deux cas rendrait notre code plus compliqué donc à la place nous utilisons [le `SafeERC20` d'OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol), qui s'assure que [tous les échecs aboutissent à un rétablissement](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L96). +Gérer les deux cas rendrait notre code plus compliqué, nous utilisons donc [`SafeERC20` d'OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol), qui s'assure que [tous les échecs entraînent une annulation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L96). ```solidity /** * @title L1StandardBridge - * @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard - * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits - * and listening to it for newly finalized withdrawals. + * @dev Le pont L1 pour ETH et ERC20 est un contrat qui stocke les fonds L1 déposés et les jetons + * standard qui sont utilisés sur la L2. Il synchronise un pont L2 correspondant, l'informant des dépôts + * et l'écoutant pour les nouveaux retraits finalisés. * */ contract L1StandardBridge is IL1StandardBridge, CrossDomainEnabled { using SafeERC20 for IERC20; ``` -Cette ligne montre comment nous spécifions d'utiliser le wrapper `SafeERC20` chaque fois que nous utilisons l'interface `IERC20`. +Cette ligne est la façon dont nous spécifions d'utiliser le wrapper `SafeERC20` chaque fois que nous utilisons l'interface `IERC20`. ```solidity /******************************** - * External Contract References * + * Références de contrats externes * ********************************/ address public l2TokenBridge; @@ -527,51 +552,66 @@ L'adresse de [L2StandardBridge](#the-l2-bridge-contract). ```solidity - // Maps L1 token to L2 token to balance of the L1 token deposited + // Mappe le jeton L1 au jeton L2 au solde du jeton L1 déposé mapping(address => mapping(address => uint256)) public deposits; ``` -Un double [mapping](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) comme celui-ci est la façon dont vous définissez un [rare tableau bidimensionnel](https://en.wikipedia.org/wiki/Sparse_matrix). Les valeurs dans cette structure de données sont identifiées comme `deposit[L1 token addr][L2 token addr]`. La valeur par défaut est zéro. Seules les cellules qui sont définies à une valeur différente sont écrites pour le stockage. +Un double [mapping](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) comme celui-ci est la manière de définir un [tableau creux à deux dimensions](https://en.wikipedia.org/wiki/Sparse_matrix). +Les valeurs dans cette structure de données sont identifiées comme `deposit[adresse jeton L1][adresse jeton L2]`. +La valeur par défaut est zéro. +Seules les cellules qui sont définies à une valeur différente sont écrites dans le stockage. ```solidity /*************** - * Constructor * + * Constructeur * ***************/ - // This contract lives behind a proxy, so the constructor parameters will go unused. + // Ce contrat vit derrière un proxy, donc les paramètres du constructeur ne seront pas utilisés. constructor() CrossDomainEnabled(address(0)) {} ``` -Pour pouvoir mettre à jour ce contrat sans avoir à copier toutes les variables dans le stockage. Pour cela, nous utilisons un [`Proxy`](https://docs.openzeppelin.com/contracts/3.x/api/proxy), un contrat qui utilise [`delegatecall`](https://solidity-by-example.org/delegatecall/) pour transférer des appels à un contact distinct dont l'adresse est stockée par le contrat de proxy (lorsque vous mettez à jour, vous ordonnez au proxy de changer cette adresse). Lorsque vous utilisez `delegatecall` le stockage reste le stockage du contrat _appelant_, donc les valeurs de toutes les variables d'état du contrat ne sont pas affectées. +Pour pouvoir mettre à jour ce contrat sans avoir à copier toutes les variables dans le stockage. +Pour ce faire, nous utilisons un [`Proxy`](https://docs.openzeppelin.com/contracts/3.x/api/proxy), un contrat qui utilise [`delegatecall`](https://solidity-by-example.org/delegatecall/) pour transférer les appels vers un contrat distinct dont l'adresse est stockée par le contrat proxy (lorsque vous mettez à jour, vous dites au proxy de changer cette adresse). +Lorsque vous utilisez `delegatecall`, le stockage reste celui du contrat _appelant_, de sorte que les valeurs de toutes les variables d'état du contrat ne sont pas affectées. -Un effet de cette pratique est que le stockage du contrat qui est _appelé_ de `delegatecall` n'est pas utilisé et donc les valeurs du constructeur qui lui sont passées n'ont pas d'importance. C'est la raison pour laquelle nous pouvons fournir une valeur absurde au constructeur `CrossDomainEnabled`. C'est aussi la raison pour laquelle l'initialisation ci-dessous est séparée du constructeur. +Un effet de ce modèle est que le stockage du contrat qui est l'_appelé_ de `delegatecall` n'est pas utilisé et donc les valeurs du constructeur qui lui sont passées n'ont pas d'importance. +C'est la raison pour laquelle nous pouvons fournir une valeur absurde au constructeur `CrossDomainEnabled`. +C'est aussi la raison pour laquelle l'initialisation ci-dessous est séparée du constructeur. ```solidity /****************** - * Initialization * + * Initialisation * ******************/ /** - * @param _l1messenger L1 Messenger address being used for cross-chain communications. - * @param _l2TokenBridge L2 standard bridge address. + * @param _l1messenger Adresse du messager L1 utilisé pour les communications inter-chaînes. + * @param _l2TokenBridge Adresse du pont de jetons L2. */ // slither-disable-next-line external-function ``` -Ce [test Slither](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) identifie les fonctions qui ne sont pas appelées à partir du code du contrat et pourrait donc être déclarées `external` au lieu de `public`. Le coût en gaz des fonctions `external` peut être diminué, car elles peuvent être fournies avec des paramètres dans le calldata. Les fonctions déclarées `public` doivent être accessibles depuis le contrat. Les contrats ne peuvent pas modifier leurs propres calldata ainsi, les paramètres doivent être en mémoire. Lorsqu'une telle fonction est appelée en externe, il est nécessaire de copier le calldata dans la mémoire, ce qui coûte du gaz. Dans ce cas, la fonction n'est appelée qu'une seule fois, donc son inefficacité n'a pas d'importance pour nous. +Ce [test Slither](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) identifie les fonctions qui ne sont pas appelées depuis le code du contrat et qui pourraient donc être déclarées `external` au lieu de `public`. +Le coût en gaz des fonctions `external` peut être inférieur, car elles peuvent être fournies avec des paramètres dans les calldata. +Les fonctions déclarées `public` doivent être accessibles depuis l'intérieur du contrat. +Les contrats ne peuvent pas modifier leurs propres calldata, donc les paramètres doivent être en mémoire. +Lorsqu'une telle fonction est appelée de l'extérieur, il est nécessaire de copier les calldata en mémoire, ce qui coûte du gaz. +Dans ce cas, la fonction n'est appelée qu'une seule fois, donc l'inefficacité n'a pas d'importance pour nous. ```solidity function initialize(address _l1messenger, address _l2TokenBridge) public { - require(messenger == address(0), "Contract has already been initialized."); + require(messenger == address(0), "Le contrat a déjà été initialisé."); ``` -La fonction `initialize` ne doit être appelée qu'une seule fois. Si l'adresse du messager inter-domaine L1 ou du jeton de connexion L2 changent, nous créons un nouveau proxy et une nouvelle passerelle qui l'appellera. Il est peu probable que cela se produise sauf lorsque le système dans son entier est mis à jour, ce qui est très rare. +La fonction `initialize` ne doit être appelée qu'une seule fois. +Si l'adresse du messager inter-domaines L1 ou du pont de jetons L2 change, nous créons un nouveau proxy et un nouveau pont qui l'appelle. +Il est peu probable que cela se produise, sauf lorsque l'ensemble du système est mis à niveau, un événement très rare. -Notez que cette fonction ne dispose d'aucun mécanisme qui délimite _qui_ peut l'appeler. Cela signifie qu'en théorie, un attaquant pourrait attendre que nous déployions le proxy et la première version de la passerelle de connexion et donc [front-run](https://solidity-by-example.org/hacks/front-running/) pour accéder à la fonction `initialize` avant que l'utilisateur légitime ne le fasse. Il existe deux méthodes pour éviter cela : +Notez que cette fonction ne dispose d'aucun mécanisme qui restreint _qui_ peut l'appeler. +Cela signifie qu'en théorie, un attaquant pourrait attendre que nous déployions le proxy et la première version du pont, puis [exécuter une attaque par front-running](https://solidity-by-example.org/hacks/front-running/) pour atteindre la fonction `initialize` avant l'utilisateur légitime. Mais il existe deux méthodes pour empêcher cela : -1. Si les contrats ne sont pas déployés directement par un EOA, mais [dans une transaction qui contient un autre contrat les créant](https://medium.com/upstate-interactive/creating-a-contract-with-a-smart-contract-bdb67c5c8595) l'ensemble du processus peut être atomique, et se terminer avant que toute autre transaction soit exécutée. -2. Si l'appel légitime à `initialize` échoue, il est toujours possible d'ignorer le proxy et la passerelle de connexion nouvellement créé et d'en créer de nouveaux. +1. Si les contrats ne sont pas déployés directement par un EOA mais [dans une transaction qui fait qu'un autre contrat les crée](https://medium.com/upstate-interactive/creating-a-contract-with-a-smart-contract-bdb67c5c8595), l'ensemble du processus peut être atomique et se terminer avant l'exécution de toute autre transaction. +2. Si l'appel légitime à `initialize` échoue, il est toujours possible d'ignorer le proxy et le pont nouvellement créés et d'en créer de nouveaux. ```solidity messenger = _l1messenger; @@ -579,39 +619,40 @@ Notez que cette fonction ne dispose d'aucun mécanisme qui délimite _qui_ peut } ``` -Ce sont les deux paramètres que la passerelle a besoin de connaître. +Ce sont les deux paramètres que le pont doit connaître. ```solidity /************** - * Depositing * + * Dépôt * **************/ - /** @dev Modifier requiring sender to be EOA. This check could be bypassed by a malicious - * contract via initcode, but it takes care of the user error we want to avoid. + /** @dev Modificateur exigeant que l'expéditeur soit un EOA. Cette vérification pourrait être contournée par un contrat + * malveillant via initcode, mais elle prend en charge l'erreur utilisateur que nous voulons éviter. */ modifier onlyEOA() { - // Used to stop deposits from contracts (avoid accidentally lost tokens) - require(!Address.isContract(msg.sender), "Account not EOA"); + // Utilisé pour arrêter les dépôts depuis des contrats (éviter la perte accidentelle de jetons) + require(!Address.isContract(msg.sender), "Compte non EOA"); _; } ``` -C'est la raison pour laquelle nous avions besoin des utilitaires d'`Address` d'OpenZeppelin. +C'est la raison pour laquelle nous avions besoin des utilitaires `Address` d'OpenZeppelin. ```solidity /** - * @dev This function can be called with no data - * to deposit an amount of ETH to the caller's balance on L2. - * Since the receive function doesn't take data, a conservative - * default amount is forwarded to L2. + * @dev Cette fonction peut être appelée sans données + * pour déposer un montant d'ETH sur le solde de l'appelant sur la L2. + * Comme la fonction de réception ne prend pas de données, un montant + * par défaut conservateur est transmis à la L2. */ receive() external payable onlyEOA { _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes("")); } ``` -Cette fonction existe à des fins de test. Notez qu'il n'apparaît pas dans les définitions d'interface - elle n'est pas prévue pour une utilisation normale. +Cette fonction existe à des fins de test. +Notez qu'elle n'apparaît pas dans les définitions d'interface - elle n'est pas destinée à un usage normal. ```solidity /** @@ -633,18 +674,18 @@ Cette fonction existe à des fins de test. Notez qu'il n'apparaît pas dans les } ``` -Ces deux fonctions sont enveloppées autour de `_initiateETHDeposit`, la fonction qui gère le dépôt réel ETH. +Ces deux fonctions sont des wrappers autour de `_initiateETHDeposit`, la fonction qui gère le dépôt réel d'ETH. ```solidity /** - * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of - * the deposit. - * @param _from Account to pull the deposit from on L1. - * @param _to Account to give the deposit to on L2. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev Exécute la logique pour les dépôts en stockant l'ETH et en informant la passerelle ETH L2 + * du dépôt. + * @param _from Compte à partir duquel retirer le dépôt sur la L1. + * @param _to Compte auquel donner le dépôt sur la L2. + * @param _l2Gas Limite de gaz requise pour finaliser le dépôt sur la L2. + * @param _data Données facultatives à transmettre à la L2. Ces données sont fournies + * uniquement pour la commodité des contrats externes. En dehors de l'application d'une longueur + * maximale, ces contrats ne fournissent aucune garantie sur leur contenu. */ function _initiateETHDeposit( address _from, @@ -652,11 +693,14 @@ Ces deux fonctions sont enveloppées autour de `_initiateETHDeposit`, la fonctio uint32 _l2Gas, bytes memory _data ) internal { - // Construct calldata for finalizeDeposit call + // Construire les calldata pour l'appel finalizeDeposit bytes memory message = abi.encodeWithSelector( ``` -La façon dont les messages transversaux fonctionnent est que le contrat de destination est appelé avec le message comme ses calldata. Les contrats Solidity interprètent toujours si leurs calldata sont conformes aux [spécifications de l'ABI](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html). La fonction Solidity [`abi.encodeWithSelector`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#abi-encoding-and-decoding-functions) crée ces calldata. +La manière dont les messages inter-domaines fonctionnent est que le contrat de destination est appelé avec le message comme calldata. +Les contrats Solidity interprètent toujours leurs calldata conformément +[aux spécifications de l'ABI](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html). +La fonction Solidity [`abi.encodeWithSelector`](https://docs.soliditylang.org/en/v0.8.12/units-and-global-variables.html#abi-encoding-and-decoding-functions) crée ces calldata. ```solidity IL2ERC20Bridge.finalizeDeposit.selector, @@ -669,24 +713,24 @@ La façon dont les messages transversaux fonctionnent est que le contrat de dest ); ``` -Le message ici est destiné à appeler [la fonction `finalizeDeposit`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141-L148) avec ces paramètres : +Le message ici est d'appeler [la fonction `finalizeDeposit`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol#L141-L148) avec ces paramètres : -| Paramètre | Valeur | Signification | -| --------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -| \_l1Token | address(0) | Valeur spéciale pour représenter ETH (qui n'est pas un jeton ERC-20) sur L1 | -| \_l2Token | Lib_PredeployAddresses.OVM_ETH | Le contrat L2 qui gère ETH sur Optimism, `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000` (ce contrat est destiné à un usage Optimism uniquement interne) | -| \_from | \_from | L'adresse sur L1 qui envoie l'ETH | -| \_to | \_to | L'adresse sur L2 qui reçoit l'ETH | -| amount | msg.value | Montant de Wei envoyé (qui a déjà été envoyé sur la passerelle) | -| \_data | \_data | Date supplémentaire à joindre au dépôt | +| Paramètre | Valeur | Signification | +| ------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| \_l1Token | adresse(0) | Valeur spéciale pour représenter l'ETH (qui n'est pas un jeton ERC-20) sur la L1 | +| \_l2Token | Lib_PredeployAddresses.OVM_ETH | Le contrat L2 qui gère l'ETH sur Optimism, `0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000` (ce contrat est destiné à un usage interne à Optimism uniquement) | +| \_from | \_from | L'adresse sur la L1 qui envoie l'ETH | +| \_to | \_to | L'adresse sur la L2 qui reçoit l'ETH | +| montant | msg.value | Montant de wei envoyé (qui a déjà été envoyé au pont) | +| \_data | \_data | Données supplémentaires à joindre au dépôt | ```solidity - // Send calldata into L2 + // Envoyer les calldata vers la L2 // slither-disable-next-line reentrancy-events sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); ``` -Envoyez le message à travers le messager inter-domaine. +Envoyer le message via le messager inter-domaines. ```solidity // slither-disable-next-line reentrancy-events @@ -694,16 +738,16 @@ Envoyez le message à travers le messager inter-domaine. } ``` -Émettre un événement pour informer toute application décentralisée qui écoute ce transfert. +Émettre un événement pour informer de ce transfert toute application décentralisée qui écoute. ```solidity /** * @inheritdoc IL1ERC20Bridge */ function depositERC20( - . - . - . + . + . + . ) external virtual onlyEOA { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data); } @@ -712,30 +756,30 @@ Envoyez le message à travers le messager inter-domaine. * @inheritdoc IL1ERC20Bridge */ function depositERC20To( - . - . - . + . + . + . ) external virtual { _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data); } ``` -Ces deux fonctions sont enveloppées autour de `_initiateERC20Deposit`, la fonction qui gère le dépôt réel ETH. +Ces deux fonctions sont des wrappers autour de `_initiateERC20Deposit`, la fonction qui gère le dépôt réel d'ERC-20. ```solidity /** - * @dev Performs the logic for deposits by informing the L2 Deposited Token - * contract of the deposit and calling a handler to lock the L1 funds. (e.g., transferFrom) + * @dev Exécute la logique des dépôts en informant le contrat du jeton déposé L2 + * du dépôt et en appelant un gestionnaire pour verrouiller les fonds L1. (par exemple, transferFrom) * - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _from Account to pull the deposit from on L1 - * @param _to Account to give the deposit to on L2 - * @param _amount Amount of the ERC20 to deposit. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @param _l1Token Adresse de l'ERC20 de L1 que nous déposons + * @param _l2Token Adresse de l'ERC20 respectif de L2 + * @param _from Compte à partir duquel retirer le dépôt sur la L1 + * @param _to Compte auquel donner le dépôt sur la L2 + * @param _amount Montant de l'ERC20 à déposer. + * @param _l2Gas Limite de gaz requise pour finaliser le dépôt sur la L2. + * @param _data Données facultatives à transmettre à la L2. Ces données sont fournies + * uniquement pour la commodité des contrats externes. En dehors de l'application d'une longueur + * maximale, ces contrats ne fournissent aucune garantie sur leur contenu. */ function _initiateERC20Deposit( address _l1Token, @@ -748,26 +792,29 @@ Ces deux fonctions sont enveloppées autour de `_initiateERC20Deposit`, la fonct ) internal { ``` -Cette fonction est similaire à `_initiateETHDeposit` ci-dessus, avec quelques différences importantes. La première différence est que cette fonction reçoit les adresses de jetons et le montant à transférer en tant que paramètres. Dans le cas d'ETH, l'appel à la passerelle comprend déjà le transfert de l'actif à la passerelle de connexion (`msg.value`). +Cette fonction est similaire à `_initiateETHDeposit` ci-dessus, avec quelques différences importantes. +La première différence est que cette fonction reçoit les adresses de jeton et le montant à transférer comme paramètres. +Dans le cas de l'ETH, l'appel au pont inclut déjà le transfert de l'actif au compte du pont (`msg.value`). ```solidity - // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future - // withdrawals. safeTransferFrom also checks if the contract has code, so this will fail if - // _from is an EOA or address(0). + // Lorsqu'un dépôt est initié sur la L1, le pont L1 transfère les fonds à lui-même pour de futurs + // retraits. safeTransferFrom vérifie également si le contrat a du code, donc cela échouera si + // _from est un EOA ou address(0). // slither-disable-next-line reentrancy-events, reentrancy-benign IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount); ``` -Les transferts de jetons ERC-20 suivent un processus différent de celui pour ETH : +Les transferts de jetons ERC-20 suivent un processus différent de celui de l'ETH : -1. L'utilisateur (`_from`) apporte une provision à la passerelle de connexion pour transférer les jetons appropriés. -2. L'utilisateur appelle la passerelle de connexion avec l'adresse du contrat de jeton, le montant, etc. -3. La passerelle transfère les jetons (à elle-même) dans le cadre du processus de dépôt. +1. L'utilisateur (`_from`) donne une autorisation au pont pour transférer les jetons appropriés. +2. L'utilisateur appelle le pont avec l'adresse du contrat de jeton, le montant, etc. +3. Le pont transfère les jetons (à lui-même) dans le cadre du processus de dépôt. -La première étape peut se produire dans une transaction séparée des deux dernières. Cependant, front-running n'est pas un problème car les deux fonctions qui appellent `_initiateERC20Deposit` (`depositERC20` et `depositERC20To`) n'appellent cette fonction qu'avec `msg.sender` en tant que paramètre `_from`. +La première étape peut se produire dans une transaction distincte des deux dernières. +Cependant, le front-running n'est pas un problème car les deux fonctions qui appellent `_initiateERC20Deposit` (`depositERC20` et `depositERC20To`) n'appellent cette fonction qu'avec `msg.sender` comme paramètre `_from`. ```solidity - // Construct calldata for _l2Token.finalizeDeposit(_to, _amount) + // Construire les calldata pour _l2Token.finalizeDeposit(_to, _amount) bytes memory message = abi.encodeWithSelector( IL2ERC20Bridge.finalizeDeposit.selector, _l1Token, @@ -778,7 +825,7 @@ La première étape peut se produire dans une transaction séparée des deux der _data ); - // Send calldata into L2 + // Envoyer les calldata vers la L2 // slither-disable-next-line reentrancy-events, reentrancy-benign sendCrossDomainMessage(l2TokenBridge, _l2Gas, message); @@ -786,7 +833,8 @@ La première étape peut se produire dans une transaction séparée des deux der deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; ``` -Ajoute le nombre de jetons déposés à la structure de données `deposits`. Il pourrait y avoir plusieurs adresses sur L2 qui correspondent au même jeton L1 ERC-20, donc il n'est pas suffisant d'utiliser le solde sur la passerelle de jetons L1 ERC-20 pour garder une trace des dépôts. +Ajouter le montant de jetons déposé à la structure de données `deposits`. +Il pourrait y avoir plusieurs adresses sur la L2 qui correspondent au même jeton ERC-20 de L1, il n'est donc pas suffisant d'utiliser le solde du pont du jeton ERC-20 de L1 pour suivre les dépôts. ```solidity @@ -795,7 +843,7 @@ Ajoute le nombre de jetons déposés à la structure de données `deposits`. Il } /************************* - * Cross-chain Functions * + * Fonctions inter-chaînes * *************************/ /** @@ -808,29 +856,30 @@ Ajoute le nombre de jetons déposés à la structure de données `deposits`. Il bytes calldata _data ``` -La passerelle de connexion L2 envoie un message au messager du domaine transversal L2 qui fait que le messager du domaine transversal L1 appelle cette fonction (lorsque la transaction [qui finalise le message](https://community.optimism.io/docs/developers/bridge/messaging/#fees-for-l2-%E2%87%92-l1-transactions) est soumise sur L1, bien sûr). +Le pont L2 envoie un message au messager inter-domaines L2, ce qui amène le messager inter-domaines L1 à appeler cette fonction (une fois que la [transaction qui finalise le message](https://community.optimism.io/docs/developers/bridge/messaging/#fees-for-l2-%E2%87%92-l1-transactions) est soumise sur la L1, bien sûr). ```solidity ) external onlyFromCrossDomainAccount(l2TokenBridge) { ``` -Assurez-vous qu'il s'agit d'un message _légitime_ provenant de la messagerie inter-domaine et originaire de la passerelle de jeton L2. Cette fonction est utilisée pour retirer l'ETH de la passerelle de connexion ainsi, nous devons nous assurer qu'elle n'est appelée que par l'appelant autorisé. +Assurez-vous qu'il s'agit d'un message _légitime_, provenant du messager inter-domaines et émanant du pont de jetons L2. +Cette fonction est utilisée pour retirer de l'ETH du pont, nous devons donc nous assurer qu'elle n'est appelée que par l'appelant autorisé. ```solidity // slither-disable-next-line reentrancy-events (bool success, ) = _to.call{ value: _amount }(new bytes(0)); ``` -Le moyen de transférer de l'ETH est d'appeler le destinataire avec le montant de wei dans le `msg.value`. +La manière de transférer de l'ETH est d'appeler le destinataire avec le montant de wei dans `msg.value`. ```solidity - require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); + require(success, "TransferHelper::safeTransferETH: le transfert d'ETH a échoué"); // slither-disable-next-line reentrancy-events emit ETHWithdrawalFinalized(_from, _to, _amount, _data); ``` -Émettre un événement à propos du retrait. +Émettre un événement concernant le retrait. ```solidity } @@ -848,7 +897,7 @@ Le moyen de transférer de l'ETH est d'appeler le destinataire avec le montant d ) external onlyFromCrossDomainAccount(l2TokenBridge) { ``` -Cette fonction est similaire à `finalizeETHWithal` ci-dessus, avec les changements nécessaires pour les jetons ERC-20. +Cette fonction est similaire à `finalizeETHWithdrawal` ci-dessus, avec les changements nécessaires pour les jetons ERC-20. ```solidity deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount; @@ -858,7 +907,7 @@ Mettre à jour la structure de données `deposits`. ```solidity - // When a withdrawal is finalized on L1, the L1 Bridge transfers the funds to the withdrawer + // Lorsqu'un retrait est finalisé sur la L1, le pont L1 transfère les fonds au retireur // slither-disable-next-line reentrancy-events IERC20(_l1Token).safeTransfer(_to, _amount); @@ -868,28 +917,35 @@ Mettre à jour la structure de données `deposits`. /***************************** - * Temporary - Migrating ETH * + * Temporaire - Migration d'ETH * *****************************/ /** - * @dev Adds ETH balance to the account. This is meant to allow for ETH - * to be migrated from an old gateway to a new gateway. - * NOTE: This is left for one upgrade only so we are able to receive the migrated ETH from the - * old contract + * @dev Ajoute un solde d'ETH au compte. Cela vise à permettre à l'ETH + * d'être migré d'une ancienne passerelle vers une nouvelle passerelle. + * NOTE : Ceci est laissé pour une seule mise à jour afin que nous puissions recevoir l'ETH migré de l'ancien + * contrat */ function donateETH() external payable {} } ``` -Il y a eu une implémentation antérieure de la passerelle. Lorsque nous sommes passés de l'implémentation à celle-ci, nous avons dû déplacer tous les actifs. Les jetons ERC-20 peuvent juste être déplacés. Cependant, pour transférer l'ETH à un contrat, vous avez besoin de l'approbation de ce contrat, ce que `donateETH` nous fournit. +Il y a eu une implémentation antérieure du pont. +Lorsque nous sommes passés de l'ancienne implémentation à celle-ci, nous avons dû déplacer tous les actifs. +Les jetons ERC-20 peuvent simplement être déplacés. +Cependant, pour transférer de l'ETH à un contrat, vous avez besoin de l'approbation de ce contrat, ce que `donateETH` nous fournit. -## Jetons ERC-20 sur L2 {#erc-20-tokens-on-l2} +## Jetons ERC-20 sur la L2 {#erc-20-tokens-on-l2} -Pour qu'un jeton ERC-20 s'intègre dans la passerelle standard, il doit permettre à la passerelle de connexion standard, et _uniquement_ la passerelle standard, de frapper des jetons. Ceci est nécessaire, car les passerelles doivent s'assurer que le nombre de jetons circulant sur Optimism est égal au nombre de jetons verrouillés à l'intérieur du contrat passerelle L1. S'il existe trop de jetons sur L2, certains utilisateurs seraient incapables de récupérer leurs actifs sur L1. Au lieu d'une passerelle de confiance, nous recréerions en fait une [banque de réserve fractionnaire](https://www.investopedia.com/terms/f/fractionalreservebanking.asp). S'il y a trop de jetons sur L1, certains de ces jetons resteraient bloqués à l'intérieur du contrat passerelle pour toujours puisqu'il n'y a aucun moyen de les libérer sans brûler les jetons L2. +Pour qu'un jeton ERC-20 s'intègre dans le pont standard, il doit permettre au pont standard, et _uniquement_ au pont standard, de frapper des jetons. +Ceci est nécessaire car les ponts doivent s'assurer que le nombre de jetons circulant sur Optimism est égal au nombre de jetons verrouillés dans le contrat du pont L1. +S'il y a trop de jetons sur la L2, certains utilisateurs ne pourraient pas ponter leurs actifs pour les ramener sur la L1. +Au lieu d'un pont de confiance, nous recréerions essentiellement un [système bancaire à réserves fractionnaires](https://www.investopedia.com/terms/f/fractionalreservebanking.asp). +S'il y a trop de jetons sur la L1, certains de ces jetons resteraient verrouillés à l'intérieur du contrat du pont pour toujours, car il n'y a aucun moyen de les libérer sans brûler des jetons L2. ### IL2StandardERC20 {#il2standarderc20} -Chaque jeton ERC-20 sur L2 qui utilise la passerelle standard doit fournir [cette interface](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/IL2StandardERC20.sol), qui possède les fonctions et les événements dont la passerelle standard a besoin. +Chaque jeton ERC-20 sur la L2 qui utilise le pont standard doit fournir [cette interface](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/IL2StandardERC20.sol), qui contient les fonctions et les événements dont le pont standard a besoin. ```solidity // SPDX-License-Identifier: MIT @@ -898,20 +954,24 @@ pragma solidity ^0.8.9; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; ``` -[L'interface standard ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) ne dispose ni des fonctions `mint` ni `burn`. Ces méthodes ne sont pas requises par [le standard ERC-20](https://eips.ethereum.org/EIPS/eip-20), qui laisse les mécanismes non spécifiés pour créer et détruire des jetons. +[L'interface standard ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) n'inclut pas les fonctions `mint` et `burn`. +Ces méthodes ne sont pas requises par [la norme ERC-20](https://eips.ethereum.org/EIPS/eip-20), qui ne spécifie pas les mécanismes de création et de destruction des jetons. ```solidity import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; ``` -[L'interface ERC-165](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol) est utilisée pour spécifier quelles fonctions sont proposées par un contrat. [Vous pouvez lire le standard ici](https://eips.ethereum.org/EIPS/eip-165). +[L'interface ERC-165](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol) est utilisée pour spécifier les fonctions qu'un contrat fournit. +[Vous pouvez lire la norme ici](https://eips.ethereum.org/EIPS/eip-165). ```solidity interface IL2StandardERC20 is IERC20, IERC165 { function l1Token() external returns (address); ``` -Cette fonction fournit l'adresse du jeton L1 qui est relié à ce contrat. Notez que nous n'avons pas de fonction similaire dans la direction opposée. Nous devons être en mesure de relier tout jeton L1, que la prise en charge L2 ait été planifiée ou non lorsqu'il a été implémenté. +Cette fonction fournit l'adresse du jeton L1 qui est ponté vers ce contrat. +Notez que nous n'avons pas de fonction similaire dans la direction opposée. +Nous devons être en mesure de ponter n'importe quel jeton L1, que la prise en charge de la L2 ait été prévue ou non lors de son implémentation. ```solidity @@ -924,11 +984,13 @@ Cette fonction fournit l'adresse du jeton L1 qui est relié à ce contrat. Notez } ``` -Les fonctions et événements à frapper (créer) et brûler (détruire) des jetons. La passerelle de connexion devrait être la seule entité qui puisse exécuter ces fonctions pour s'assurer que le nombre de jetons est correct (égal au nombre de jetons verrouillés sur L1). +Fonctions et événements pour frapper (créer) et brûler (détruire) des jetons. +Le pont doit être la seule entité pouvant exécuter ces fonctions pour garantir que le nombre de jetons est correct (égal au nombre de jetons verrouillés sur la L1). ### L2StandardERC20 {#L2StandardERC20} -[Ceci est notre implémentation de l'interface `IL2StandardERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/L2StandardERC20.sol). À moins que vous n'ayez besoin d'une sorte de logique personnalisée, vous devriez utiliser celle-ci. +[Ceci est notre implémentation de l'interface `IL2StandardERC20`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/standards/L2StandardERC20.sol). +À moins que vous n'ayez besoin d'une logique personnalisée, vous devriez utiliser celle-ci. ```solidity // SPDX-License-Identifier: MIT @@ -937,7 +999,8 @@ pragma solidity ^0.8.9; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; ``` -[Le contrat OpenZeppelin ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). Optimism ne souhaite pas réinventer de la roue, surtout lorsque la roue est bien contrôlée et a besoin d'être suffisamment digne de confiance pour détenir des actifs. +[Le contrat ERC-20 d'OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Optimism ne croit pas à la réinvention de la roue, surtout lorsque la roue est bien auditée et doit être suffisamment fiable pour détenir des actifs. ```solidity import "./IL2StandardERC20.sol"; @@ -947,15 +1010,15 @@ contract L2StandardERC20 is IL2StandardERC20, ERC20 { address public l2Bridge; ``` -Ce sont les deux paramètres de configuration supplémentaires dont nous avons besoin et que ERC-20 ne réalise normalement pas. +Ce sont les deux paramètres de configuration supplémentaires que nous exigeons et qu'un ERC-20 ne requiert normalement pas. ```solidity /** - * @param _l2Bridge Address of the L2 standard bridge. - * @param _l1Token Address of the corresponding L1 token. - * @param _name ERC20 name. - * @param _symbol ERC20 symbol. + * @param _l2Bridge Adresse du pont standard L2. + * @param _l1Token Adresse du jeton L1 correspondant. + * @param _name Nom ERC20. + * @param _symbol Symbole ERC20. */ constructor( address _l2Bridge, @@ -968,12 +1031,12 @@ Ce sont les deux paramètres de configuration supplémentaires dont nous avons b } ``` -En premier lieu, appelez le constructeur pour le contrat dont nous héritons de (`ERC20(_name, _symbol)`) et définissez ensuite vos propres variables. +Appelez d'abord le constructeur pour le contrat dont nous héritons (`ERC20(_name, _symbol)`) puis définissez nos propres variables. ```solidity modifier onlyL2Bridge() { - require(msg.sender == l2Bridge, "Only L2 Bridge can mint and burn"); + require(msg.sender == l2Bridge, "Seul le pont L2 peut frapper et brûler"); _; } @@ -988,11 +1051,12 @@ En premier lieu, appelez le constructeur pour le contrat dont nous héritons de } ``` -C'est ainsi que fonctionne [ERC-165](https://eips.ethereum.org/EIPS/eip-165). Chaque interface est un certain nombre de fonctions supportées, et est identifiée comme ABI[exclusive ou](https://en.wikipedia.org/wiki/Exclusive_or) des [sélecteurs de fonctions ABI](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html#function-selector) pour ces fonctions. +C'est ainsi que [l'ERC-165](https://eips.ethereum.org/EIPS/eip-165) fonctionne. +Chaque interface est un certain nombre de fonctions prises en charge, et est identifiée comme le [ou exclusif](https://en.wikipedia.org/wiki/Exclusive_or) des [sélecteurs de fonction ABI](https://docs.soliditylang.org/en/v0.8.12/abi-spec.html#function-selector) de ces fonctions. -La passerelle de connexion L2 utilise ERC-165 comme vérification garantissant que le contrat ERC-20 auquel des actifs sont envoyés est un `IL2StandardERC20`. +Le pont L2 utilise l'ERC-165 comme une vérification de bon sens pour s'assurer que le contrat ERC-20 auquel il envoie des actifs est un `IL2StandardERC20`. -**Remarque :** Il n'existe rien pour empêcher le contrat dévoyé de fournir de fausses réponses à `supportsInterface` ainsi, il s'agit donc d'un mécanisme de vérification et non _pas_ un mécanisme de sécurité. +**Note :** Rien n'empêche un contrat malveillant de fournir de fausses réponses à `supportsInterface`, il s'agit donc d'un mécanisme de vérification de bon sens, _pas_ d'un mécanisme de sécurité. ```solidity // slither-disable-next-line external-function @@ -1011,13 +1075,15 @@ La passerelle de connexion L2 utilise ERC-165 comme vérification garantissant q } ``` -Seul la passerelle L2 est autorisée à frapper et à brûler des actifs. +Seul le pont L2 est autorisé à frapper et brûler des actifs. -`_mint` et `_burn` sont définis dans le contrat [OpenZeppelin ERC-20](/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn). Ce contrat ne les expose pas en externe, parce que les conditions de frappe et de brûlage des jetons sont aussi variées que le nombre de façons d'utiliser ERC-20. +`_mint` et `_burn` sont en fait définis dans le [contrat ERC-20 d'OpenZeppelin](/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn). +Ce contrat ne les expose tout simplement pas à l'extérieur, car les conditions pour frapper et brûler des jetons sont aussi variées que le nombre de façons d'utiliser l'ERC-20. -## Code de passerelle L2 {#l2-bridge-code} +## Code du pont L2 {#l2-bridge-code} -Il s'agit du code qui exécute la passerelle de connexion sur Optimism. [La source de ce contrat se trouve ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol). +C'est le code qui exécute le pont sur Optimism. +[La source de ce contrat est ici](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol). ```solidity // SPDX-License-Identifier: MIT @@ -1029,10 +1095,13 @@ import { IL1ERC20Bridge } from "../../L1/messaging/IL1ERC20Bridge.sol"; import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol"; ``` -L'interface [IL2ERC20Bridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) est très similaire à [l'équivalent L1](#IL1ERC20Bridge) que nous avons vu ci-dessus. Il existe deux différences significatives : +L'interface [IL2ERC20Bridge](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol) est très similaire à l'[équivalent L1](#IL1ERC20Bridge) que nous avons vu plus haut. +Il y a deux différences significatives : -1. Sur L1, vous initiez des dépôts et finalisez des retraits. Ici, vous initiez des retraits et finalisez les dépôts. -2. Sur L1, il est nécessaire de faire une distinction entre les jetons ETH et ERC-20. Sur L2, nous pouvons utiliser les mêmes fonctions pour les deux, parce que les soldes ETH en interne sur Optimism sont traités comme un jeton ERC-20 avec l'adresse [0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000](https://optimistic.etherscan.io/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000). +1. Sur la L1, vous initiez les dépôts et finalisez les retraits. + Ici, vous initiez les retraits et finalisez les dépôts. +2. Sur la L1, il est nécessaire de distinguer l'ETH des jetons ERC-20. + Sur la L2, nous pouvons utiliser les mêmes fonctions pour les deux car, en interne, les soldes d'ETH sur Optimism sont gérés comme un jeton ERC-20 avec l'adresse [0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000](https://explorer.optimism.io/address/0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000). ```solidity /* Library Imports */ @@ -1045,32 +1114,33 @@ import { IL2StandardERC20 } from "../../standards/IL2StandardERC20.sol"; /** * @title L2StandardBridge - * @dev The L2 Standard bridge is a contract which works together with the L1 Standard bridge to - * enable ETH and ERC20 transitions between L1 and L2. - * This contract acts as a minter for new tokens when it hears about deposits into the L1 Standard - * bridge. - * This contract also acts as a burner of the tokens intended for withdrawal, informing the L1 - * bridge to release L1 funds. + * @dev Le pont standard L2 est un contrat qui fonctionne en collaboration avec le pont standard L1 pour + * permettre les transitions d'ETH et d'ERC20 entre la L1 et la L2. + * Ce contrat agit comme un frappeur de nouveaux jetons lorsqu'il est informé de dépôts dans le pont standard L1. + * Ce contrat agit également comme un brûleur de jetons destinés au retrait, informant le pont L1 + * de libérer les fonds L1. */ contract L2StandardBridge is IL2ERC20Bridge, CrossDomainEnabled { /******************************** - * External Contract References * + * Références de contrats externes * ********************************/ address public l1TokenBridge; ``` -Conserver la trace de l'adresse de la passerelle de connexion L1. Notez que contrairement à l'équivalent pour L1, ici nous \_ avons besoin \_de cette variable. L'adresse de la passerelle L1 n'est pas connue à l'avance. +Garder une trace de l'adresse du pont L1. +Notez que contrairement à l'équivalent L1, ici nous _avons besoin_ de cette variable. +L'adresse du pont L1 n'est pas connue à l'avance. ```solidity /*************** - * Constructor * + * Constructeur * ***************/ /** - * @param _l2CrossDomainMessenger Cross-domain messenger used by this contract. - * @param _l1TokenBridge Address of the L1 bridge deployed to the main chain. + * @param _l2CrossDomainMessenger Messager inter-domaines utilisé par ce contrat. + * @param _l1TokenBridge Adresse du pont L1 déployé sur la chaîne principale. */ constructor(address _l2CrossDomainMessenger, address _l1TokenBridge) CrossDomainEnabled(_l2CrossDomainMessenger) @@ -1079,7 +1149,7 @@ Conserver la trace de l'adresse de la passerelle de connexion L1. Notez que cont } /*************** - * Withdrawing * + * Retrait * ***************/ /** @@ -1108,21 +1178,23 @@ Conserver la trace de l'adresse de la passerelle de connexion L1. Notez que cont } ``` -Ces deux fonctions initient les retraits. Notez qu'il n'y a pas besoin de spécifier l'adresse de jeton L1. Les jetons L2 doivent nous indiquer l'adresse de l'équivalent L1. +Ces deux fonctions initient les retraits. +Notez qu'il n'est pas nécessaire de spécifier l'adresse du jeton L1. +Les jetons L2 sont censés nous indiquer l'adresse de l'équivalent L1. ```solidity /** - * @dev Performs the logic for withdrawals by burning the token and informing - * the L1 token Gateway of the withdrawal. - * @param _l2Token Address of L2 token where withdrawal is initiated. - * @param _from Account to pull the withdrawal from on L2. - * @param _to Account to give the withdrawal to on L1. - * @param _amount Amount of the token to withdraw. - * @param _l1Gas Unused, but included for potential forward compatibility considerations. - * @param _data Optional data to forward to L1. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. + * @dev Exécute la logique des retraits en brûlant le jeton et en informant + * la passerelle de jetons L1 du retrait. + * @param _l2Token Adresse du jeton L2 où le retrait est initié. + * @param _from Compte à partir duquel retirer le retrait sur la L2. + * @param _to Compte auquel donner le retrait sur la L1. + * @param _amount Montant du jeton à retirer. + * @param _l1Gas Inutilisé, mais inclus pour des considérations de compatibilité ascendante potentielles. + * @param _data Données facultatives à transmettre à la L1. Ces données sont fournies + * uniquement pour la commodité des contrats externes. En dehors de l'application d'une longueur + * maximale, ces contrats ne fournissent aucune garantie sur leur contenu. */ function _initiateWithdrawal( address _l2Token, @@ -1132,17 +1204,17 @@ Ces deux fonctions initient les retraits. Notez qu'il n'y a pas besoin de spéci uint32 _l1Gas, bytes calldata _data ) internal { - // When a withdrawal is initiated, we burn the withdrawer's funds to prevent subsequent L2 - // usage + // Lorsqu'un retrait est initié, nous brûlons les fonds du retireur pour empêcher une utilisation ultérieure sur la L2 + // // slither-disable-next-line reentrancy-events IL2StandardERC20(_l2Token).burn(msg.sender, _amount); ``` -Notez que nous ne sommes _pas_ reliés au paramètre `_from` mais à `msg.sender` qui est beaucoup plus difficile à contrefaire (même impossible, à ma connaissance). +Notez que nous ne nous fions _pas_ au paramètre `_from` mais à `msg.sender` qui est beaucoup plus difficile à falsifier (impossible, à ma connaissance). ```solidity - // Construct calldata for l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) + // Construire les calldata pour l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) // slither-disable-next-line reentrancy-events address l1Token = IL2StandardERC20(_l2Token).l1Token(); bytes memory message; @@ -1150,7 +1222,7 @@ Notez que nous ne sommes _pas_ reliés au paramètre `_from` mais à `msg.sender if (_l2Token == Lib_PredeployAddresses.OVM_ETH) { ``` -Sur L1, il est nécessaire de faire une distinction entre les jetons ETH et ERC-20. +Sur la L1, il est nécessaire de distinguer l'ETH de l'ERC-20. ```solidity message = abi.encodeWithSelector( @@ -1172,7 +1244,7 @@ Sur L1, il est nécessaire de faire une distinction entre les jetons ETH et ERC- ); } - // Send message up to L1 bridge + // Envoyer le message au pont L1 // slither-disable-next-line reentrancy-events sendCrossDomainMessage(l1TokenBridge, _l1Gas, message); @@ -1181,7 +1253,7 @@ Sur L1, il est nécessaire de faire une distinction entre les jetons ETH et ERC- } /************************************ - * Cross-chain Function: Depositing * + * Fonction inter-chaînes : Dépôt * ************************************/ /** @@ -1196,69 +1268,71 @@ Sur L1, il est nécessaire de faire une distinction entre les jetons ETH et ERC- bytes calldata _data ``` -Cette fonction est appelée via `L1StandardBridge`. +Cette fonction est appelée par `L1StandardBridge`. ```solidity ) external virtual onlyFromCrossDomainAccount(l1TokenBridge) { ``` -Assurez-vous que la source du message est fiable. C'est important car cette fonction appelle `_mint` et peut être utilisée pour donner des jetons qui ne sont pas couverts par des jetons que la passerelle possède sur L1. +Assurez-vous que la source du message est légitime. +C'est important car cette fonction appelle `_mint` et pourrait être utilisée pour donner des jetons qui ne sont pas couverts par des jetons que le pont possède sur la L1. ```solidity - // Check the target token is compliant and - // verify the deposited token on L1 matches the L2 deposited token representation here + // Vérifier que le jeton cible est conforme et + // vérifier que le jeton déposé sur la L1 correspond à la représentation du jeton déposé sur la L2 ici if ( // slither-disable-next-line reentrancy-events ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) && _l1Token == IL2StandardERC20(_l2Token).l1Token() ``` -Contrôle de cohérence : +Vérifications de bon sens : 1. L'interface correcte est prise en charge -2. L'adresse du contrat L2 ERC-20 du L1 correspond à la source L1 des jetons +2. L'adresse L1 du contrat ERC-20 L2 correspond à la source L1 des jetons ```solidity ) { - // When a deposit is finalized, we credit the account on L2 with the same amount of - // tokens. + // Lorsqu'un dépôt est finalisé, nous créditons le compte sur la L2 avec le même montant + // de jetons. // slither-disable-next-line reentrancy-events IL2StandardERC20(_l2Token).mint(_to, _amount); // slither-disable-next-line reentrancy-events emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); ``` -Si les contrôles de cohérence sont effectués, finalisez le dépôt : +Si les vérifications de bon sens sont concluantes, finalisez le dépôt : 1. Frapper les jetons 2. Émettre l'événement approprié ```solidity } else { - // Either the L2 token which is being deposited-into disagrees about the correct address - // of its L1 token, or does not support the correct interface. - // This should only happen if there is a malicious L2 token, or if a user somehow - // specified the wrong L2 token address to deposit into. - // In either case, we stop the process here and construct a withdrawal - // message so that users can get their funds out in some cases. - // There is no way to prevent malicious token contracts altogether, but this does limit - // user error and mitigate some forms of malicious contract behavior. + // Soit le jeton L2 dans lequel le dépôt est effectué est en désaccord sur l'adresse correcte + // de son jeton L1, soit il ne prend pas en charge l'interface correcte. + // Cela ne devrait se produire que s'il y a un jeton L2 malveillant, ou si un utilisateur a + // spécifié la mauvaise adresse de jeton L2 pour le dépôt. + // Dans tous les cas, nous arrêtons le processus ici et construisons un message de retrait + // afin que les utilisateurs puissent récupérer leurs fonds dans certains cas. + // Il n'y a aucun moyen d'empêcher complètement les contrats de jetons malveillants, mais cela limite + // les erreurs des utilisateurs et atténue certaines formes de comportement de contrat malveillant. ``` -Si un utilisateur a fait une erreur détectable en utilisant la mauvaise adresse de jeton L2, nous souhaitons annuler le dépôt et retourner les jetons sur L1. La seule façon de le faire à partir de L2 est d'envoyer un message qui devra attendre la période problématique de défaut mais c'est beaucoup mieux pour l'utilisateur que de perdre les jetons définitivement. +Si un utilisateur a commis une erreur détectable en utilisant la mauvaise adresse de jeton L2, nous voulons annuler le dépôt et retourner les jetons sur la L1. +La seule façon de le faire depuis la L2 est d'envoyer un message qui devra attendre la période de contestation des erreurs, mais c'est bien mieux pour l'utilisateur que de perdre les jetons de manière permanente. ```solidity bytes memory message = abi.encodeWithSelector( IL1ERC20Bridge.finalizeERC20Withdrawal.selector, _l1Token, _l2Token, - _to, // switched the _to and _from here to bounce back the deposit to the sender + _to, // a inversé le _to et le _from ici pour renvoyer le dépôt à l'expéditeur _from, _amount, _data ); - // Send message up to L1 bridge + // Envoyer le message au pont L1 // slither-disable-next-line reentrancy-events sendCrossDomainMessage(l1TokenBridge, 0, message); // slither-disable-next-line reentrancy-events @@ -1270,8 +1344,13 @@ Si un utilisateur a fait une erreur détectable en utilisant la mauvaise adresse ## Conclusion {#conclusion} -La passerelle standard est le mécanisme le plus souple pour les transferts d'actifs. Cependant, parce qu'il est si générique, ce n'est pas toujours le mécanisme le plus facile à utiliser. Spécialement pour les retraits, la plupart des utilisateurs préfèrent utiliser des [passerelles tierces](https://optimism.io/apps#bridge) qui n'attendent pas la période problématique et ne nécessitent pas de preuve de Merkle pour finaliser le retrait. +Le pont standard est le mécanisme le plus flexible pour les transferts d'actifs. +Cependant, parce qu'il est si générique, ce n'est pas toujours le mécanisme le plus facile à utiliser. +En particulier pour les retraits, la plupart des utilisateurs préfèrent utiliser des [ponts tiers](https://optimism.io/apps#bridge) qui n'attendent pas la période de contestation et ne nécessitent pas de preuve de Merkle pour finaliser le retrait. + +Ces ponts fonctionnent généralement en ayant des actifs sur la L1, qu'ils fournissent immédiatement moyennant de faibles frais (souvent inférieurs au coût du gaz pour un retrait via un pont standard). +Lorsque le pont (ou les personnes qui le gèrent) anticipe un manque d'actifs sur la L1, il transfère des actifs suffisants depuis la L2. Comme il s'agit de retraits très importants, le coût du retrait est amorti sur un montant élevé et représente un pourcentage beaucoup plus faible. -Ces passerelles fonctionnent généralement en ayant des actifs sur L1 qu'ils fournissent immédiatement moyennant un petit supplément (souvent inférieur au coût du gaz pour un retrait standard d'une passerelle). Quand la passerelle (ou la personne qui la gère) prévoit d'être en deçà des actifs L1, elle transfère suffisamment d'actifs de L2. Comme il s'agit de retraits très importants, le coût de retrait est amorti sur une somme importante et représente un pourcentage beaucoup plus faible. +Espérons que cet article vous a aidé à mieux comprendre le fonctionnement de la couche 2, et comment écrire du code Solidity clair et sécurisé. -Espérons que cet article vous aide à mieux comprendre comment la couche 2 fonctionne, et comment écrire du code Solidity clair et sécurisé. +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/reverse-engineering-a-contract/index.md b/public/content/translations/fr/developers/tutorials/reverse-engineering-a-contract/index.md index e1e7926cfb7..be53e2027a1 100644 --- a/public/content/translations/fr/developers/tutorials/reverse-engineering-a-contract/index.md +++ b/public/content/translations/fr/developers/tutorials/reverse-engineering-a-contract/index.md @@ -3,307 +3,305 @@ title: "Ingénierie inverse d'un contrat" description: Comment comprendre un contrat quand vous n'avez pas le code source author: Ori Pomerantz lang: fr -tags: - - "evm" - - "codes d'opérations" +tags: [ "evm", "opcodes" ] skill: advanced published: 2021-12-30 --- ## Introduction {#introduction} -_Il n'y a aucun secret sur la blockchain_, tout ce qui se passe est cohérent, vérifiable et accessible au public. Idéalement, [les contrats devraient avoir leur code source publié et vérifié sur Etherscan](https://etherscan.io/address/0xb8901acb165ed027e32754e0ffe830802919727f#code). Or [ce n'est pas toujours le cas](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#code). Dans cet article, vous apprendrez comment rétro-concevoir des contrats en prenant comme exemple un contrat sans code source, [`0x2510c039cc3b061d79e564b38836da87e31b342f`](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f). +_Il n'y a pas de secrets sur la blockchain_, tout ce qui se passe est cohérent, vérifiable et accessible au public. Idéalement, [les contrats devraient avoir leur code source publié et vérifié sur Etherscan](https://etherscan.io/address/0xb8901acb165ed027e32754e0ffe830802919727f#code). Cependant, [ce n'est pas toujours le cas](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#code). Dans cet article, vous apprendrez à faire de l'ingénierie inverse sur des contrats en examinant un contrat sans code source, [`0x2510c039cc3b061d79e564b38836da87e31b342f`](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f). -Il existe des décompilateurs, mais ils ne produisent pas toujours [des résultats utilisables](https://etherscan.io/bytecode-decompiler?a=0x2510c039cc3b061d79e564b38836da87e31b342f). Dans cet article, vous apprendrez comment rétro-concevoir manuellement et comprendre un contrat grâce aux [opcodes](https://github.com/wolflo/evm-opcodes) et également comment interpréter les résultats du décompilateur. +Il existe des compilateurs inverses, mais ils ne produisent pas toujours des [résultats utilisables](https://etherscan.io/bytecode-decompiler?a=0x2510c039cc3b061d79e564b38836da87e31b342f). Dans cet article, vous apprendrez comment faire manuellement de l'ingénierie inverse et comprendre un contrat à partir [des opcodes](https://github.com/wolflo/evm-opcodes), ainsi que comment interpréter les résultats d'un décompilateur. -Pour être en mesure de comprendre cet article, vous devriez connaître les bases de l'EVM et être au moins familier avec l'assembleur EVM. [Vous pouvez en savoir plus sur ces sujets ici](https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e). +Pour pouvoir comprendre cet article, vous devez déjà connaître les bases de l'EVM et être au moins quelque peu familier avec l'assembleur EVM. [Vous pouvez vous renseigner sur ces sujets ici](https://medium.com/mycrypto/the-ethereum-virtual-machine-how-does-it-work-9abac2b7c9e). -## Préparer le Code Exécutable {#prepare-the-executable-code} +## Préparer le code exécutable {#prepare-the-executable-code} -Vous pouvez récupérer les opcodes en cherchant le contrat sur Etherscan, cliquez sur l'onglet **Contract** et puis sur **Switch to Opcodes View**. Vous obtenez un affichage d'un opcode par ligne. +Vous pouvez obtenir les opcodes en allant sur Etherscan pour le contrat, en cliquant sur l'onglet **Contract** puis sur **Switch to Opcodes View**. Vous obtenez une vue avec un opcode par ligne. -![Vue Opcode depuis Etherscan](opcode-view.png) +![Vue des opcodes depuis Etherscan](opcode-view.png) -Pour être capable de comprendre les sauts, vous devez savoir où se trouve chaque opcode dans le code. Pour ce faire, vous pouvez ouvrir Google Spreadsheet et coller les codes d'opération dans le colonne C.[Vous pouvez sauter les étapes suivantes en faisant une copie de cette feuille de calcul](https://docs.google.com/spreadsheets/d/1tKmTJiNjUwHbW64wCKOSJxHjmh0bAUapt6btUYE7kDA/edit?usp=sharing). +Toutefois, pour comprendre les sauts, vous devez savoir où se trouve chaque opcode dans le code. Pour ce faire, une solution consiste à ouvrir une feuille de calcul Google et à coller les opcodes dans la colonne C. [Vous pouvez sauter les étapes suivantes en faisant une copie de cette feuille de calcul déjà préparée](https://docs.google.com/spreadsheets/d/1tKmTJiNjUwHbW64wCKOSJxHjmh0bAUapt6btUYE7kDA/edit?usp=sharing). -L'étape suivante est d'obtenir les emplacements corrects dans le code pour comprendre les sauts. Nous allons mettre la taille du code d'opération dans la colonne B et son emplacement (en hexadécimal) dans la colonne A. Entrez cette fonction dans la cellule `B1` et puis copiez-collez sur le reste de la colonne B jusqu'à la fin du code. Après cela, vous pouvez masquer la colonne B. +L'étape suivante consiste à obtenir les emplacements de code corrects afin de pouvoir comprendre les sauts. Nous allons mettre la taille de l'opcode dans la colonne B, et l'emplacement (en hexadécimal) dans la colonne A. Tapez cette fonction dans la cellule `B1`, puis copiez-la et collez-la pour le reste de la colonne B, jusqu'à la fin du code. Après cela, vous pouvez masquer la colonne B. ``` =1+IF(REGEXMATCH(C1,"PUSH"),REGEXEXTRACT(C1,"PUSH(\d+)"),0) ``` -Tout d'abord, cette fonction ajoute un octet au code d'opération, puis elle recherche le mot clé `PUSH`. Les codes d'opération PUSH sont spéciaux car ils doivent avoir des octets supplémentaires pour la valeur qui doit être poussée. Si l'opcode est un `PUSH`, nous extrayons le nombre d'octets et ajoutons la valeur à la taille de l'opcode. +D'abord, cette fonction ajoute un octet pour l'opcode lui-même, puis recherche `PUSH`. Les opcodes Push sont spéciaux, car ils nécessitent des octets supplémentaires pour la valeur poussée. Si l'opcode est un `PUSH`, nous extrayons le nombre d'octets et nous l'ajoutons. -Dans la cellule `A1`, déclarez la première valeur décalée à 0. Puis, dans la cellule `A2`, entrez cette fonction et copiez-collez la sur le reste de la colonne A : +Dans `A1` mettez le premier décalage, zéro. Ensuite, dans `A2`, mettez cette fonction et copiez-collez la de nouveau pour le reste de la colonne A : ``` =dec2hex(hex2dec(A1)+B1) ``` -Nous avons besoin de cette fonction pour nous donner la valeur hexadécimale car les valeurs poussées avant les sauts (`JUMP` et `JUMPI`) nous sont données en hexadécimal. +Nous avons besoin de cette fonction pour nous donner la valeur hexadécimale, car les valeurs qui sont poussées avant les sauts (`JUMP` et `JUMPI`) nous sont données en hexadécimal. -## Le Point d'Entrée (0x00) {#the-entry-point-0x00} +## Le point d'entrée (0x00) {#the-entry-point-0x00} -Les contrats sont toujours exécutés à partir du premier octet. Ceci est la première partie du code : +Les contrats sont toujours exécutés à partir du premier octet. Ceci est la partie initiale du code : -| Décalage | Opcode | Pile (après le code d'opération) | -| --------:| ------------ | -------------------------------- | -| 0 | PUSH1 0x80 | 0x80 | -| 2 | PUSH1 0x40 | 0x40, 0x80 | -| 4 | MSTORE | Vide | -| 5 | PUSH1 0x04 | 0x04 | -| 7 | CALLDATASIZE | CALLDATASIZE 0x04 | -| 8 | LT | CALLDATASIZE\<4 | -| 9 | PUSH2 0x005e | 0x5E CALLDATASIZE\<4 | -| C | JUMPI | Vide | +| Décalage | Opcode | Pile (après l'opcode) | +| -------: | ------------ | -------------------------------------------- | +| 0 | PUSH1 0x80 | 0x80 | +| 2 | PUSH1 0x40 | 0x40, 0x80 | +| 4 | MSTORE | Vide | +| 5 | PUSH1 0x04 | 0x04 | +| 7 | CALLDATASIZE | CALLDATASIZE 0x04 | +| 8 | LT | CALLDATASIZE<4 | +| 9 | PUSH2 0x005e | 0x5E CALLDATASIZE<4 | +| C | JUMPI | Vide | Ce code fait deux choses : -1. Écrit 0x80 sous la forme d'une valeur de 32 octets sur des emplacements de mémoire 0x40-0x5F (0x80 est stocké dans 0x5F, et 0x40-0x5E sont à zéro). -2. Lire la taille des données d'appel. Normalement, les données d'appel pour un contrat Ethereum suivent [l'ABI (interface binaire-programme)](https://docs.soliditylang.org/en/v0.8.10/abi-spec.html), qui nécessite au minimum quatre octets pour le sélecteur de fonction. Si la taille des données d'appel est inférieure à quatre, il y a un saut à 0x5E. +1. Écrit 0x80 comme une valeur de 32 octets aux emplacements mémoire 0x40-0x5F (0x80 est stocké dans 0x5F, et 0x40-0x5E sont tous des zéros). +2. Lit la taille des données d'appel. Normalement, les données d'appel pour un contrat Ethereum suivent [l'ABI (interface binaire d'application)](https://docs.soliditylang.org/en/v0.8.10/abi-spec.html), qui nécessite au minimum quatre octets pour le sélecteur de fonction. Si la taille des données d'appel est inférieure à quatre, sautez à 0x5E. -![Organigramme de cette partie](flowchart-entry.png) +![Organigramme pour cette partie](flowchart-entry.png) -### Le Gestionnaire à 0x5E (pour les données d'appel non-ABI) {#the-handler-at-0x5e-for-non-abi-call-data} +### Le gestionnaire à 0x5E (pour les données d'appel non-ABI) {#the-handler-at-0x5e-for-non-abi-call-data} | Décalage | Opcode | -| --------:| ------------ | +| -------: | ------------ | | 5E | JUMPDEST | | 5F | CALLDATASIZE | | 60 | PUSH2 0x007c | | 63 | JUMPI | -Ce snippet commence avec un `JUMPDEST`. Les programmes EVM (machine virtuelle Ethereum) lèvent une exception si l'on saute à un code d'opération qui n'est pas `JUMPDEST`. Puis, il vérifie le CALLDATASIZE et si c'est « true » (c'est-à-dire, si ce n'est pas zéro), il saute à 0x7C. Nous verrons ça ci-dessous. +Cet extrait de code commence par un `JUMPDEST`. Les programmes EVM (machine virtuelle Ethereum) lèvent une exception si vous sautez vers un opcode qui n'est pas `JUMPDEST`. Ensuite, il examine le CALLDATASIZE, et si c'est « vrai » (c'est-à-dire non nul), il saute à 0x7C. Nous y reviendrons ci-dessous. -| Décalage | Opcode | Pile (après l'opcode) | -| --------:| ---------- | -------------------------------------------------------------------------- | +| Décalage | Opcode | Pile (après l'opcode) | +| -------: | ---------- | ------------------------------------------------------------------------------------------ | | 64 | CALLVALUE | [Wei](/glossary/#wei) fourni par l'appel. Appelé `msg.value` dans Solidity | -| 65 | PUSH1 0x06 | 6 CALLVALUE | -| 67 | PUSH1 0x00 | 0 6 CALLVALUE | -| 69 | DUP3 | CALLVALUE 0 6 CALLVALUE | -| 6A | DUP3 | 6 CALLVALUE 0 6 CALLVALUE | -| 6B | SLOAD | Storage[6] CALLVALUE 0 6 CALLVALUE | +| 65 | PUSH1 0x06 | 6 CALLVALUE | +| 67 | PUSH1 0x00 | 0 6 CALLVALUE | +| 69 | DUP3 | CALLVALUE 0 6 CALLVALUE | +| 6A | DUP3 | 6 CALLVALUE 0 6 CALLVALUE | +| 6B | SLOAD | Stockage[6] CALLVALUE 0 6 CALLVALUE | -Ainsi, lorsqu'il n'y a pas de données d'appel, nous lisons la valeur de Stockage[6]. Nous ne savons pas encore quelle est cette valeur, mais nous pouvons rechercher les transactions que le contrat a reçues sans aucune donnée d'appel. Les transactions qui transfèrent juste de l'ETH sans aucune donnée d'appel (et donc aucune méthode) disposent de la méthode `Transfer` dans Etherscan. En fait, [la toute première transaction reçue par le contrat](https://etherscan.io/tx/0xeec75287a583c36bcc7ca87685ab41603494516a0f5986d18de96c8e630762e7) est un transfert. +Ainsi, lorsqu'il n'y a pas de données d'appel, nous lisons la valeur de Stockage[6]. Nous ne savons pas encore quelle est cette valeur, mais nous pouvons rechercher les transactions que le contrat a reçues sans données d'appel. Les transactions qui ne font que transférer des ETH sans données d'appel (et donc sans méthode) ont dans Etherscan la méthode `Transfer`. En fait, [la toute première transaction que le contrat a reçue](https://etherscan.io/tx/0xeec75287a583c36bcc7ca87685ab41603494516a0f5986d18de96c8e630762e7) est un transfert. -Si nous regardons la transaction et que nous cliquons sur **Click to see More**, nous voyons que l'appel de donnée, appelé une donnée d'entrée, est en fait vide (`0x`). Notez aussi que la valeur est à 1.559 ETH, ce qui sera intéressant plus tard. +Si nous examinons cette transaction et cliquons sur **Click to see More**, nous voyons que les données d'appel, appelées données d'entrée, sont en effet vides (`0x`). Notez également que la valeur est de 1,559 ETH, ce qui sera pertinent plus tard. ![Les données d'appel sont vides](calldata-empty.png) -Ensuite, cliquez sur l'onglet **State** et développez le contrat que nous rétro-concevons (0x2510...). Vous pouvez voir que `Stockage[6]` a changé pendant la transaction, et si vous changez l'hexadécimal en **Numéro**, vous voyez que la valeur transférée est maintenant affichée en wei : 1,559,000,000,000,000 (j'ai ajouté les virgules à des fins de clarté), correspondant à la valeur du prochain contrat. +Ensuite, cliquez sur l'onglet **State** et développez le contrat sur lequel nous effectuons de l'ingénierie inverse (0x2510...). Vous pouvez voir que `Stockage[6]` a bien changé pendant la transaction, et si vous passez de Hex à **Number**, vous verrez que la valeur est devenue 1 559 000 000 000 000 000, la valeur transférée en wei (j'ai ajouté les virgules pour plus de clarté), correspondant à la valeur du contrat suivante. ![Le changement dans Stockage[6]](storage6.png) -Si nous regardons dans les changements d'état causés par [d'autres transactions `Transfer` de la même période](https://etherscan.io/tx/0xf708d306de39c422472f43cb975d97b66fd5d6a6863db627067167cbf93d84d1#statechange) nous voyons que `Storage[6]` a suivi la valeur du contrat pendant un certain temps. Pour le moment, nous l'appellerons `Valeur*`. L'astérisque (`*`) nous rappelle que nous ne _savons_ pas ce que cette variable fait pour le moment, mais ça ne peut pas être simplement de tracer la valeur du contrat parce qu'il n'y a pas besoin d'utiliser le stockage, qui est très cher, quand vous pouvez obtenir le solde de vos comptes à l'aide de `ADDRESS BALANCE`. Le premier code d'opérations dévoile la propre adresse du contrat. Le deuxième lit l'adresse en haut de la pile et la remplace par le solde de cette adresse. +Si nous examinons les changements d'état causés par [d'autres transactions `Transfer` de la même période](https://etherscan.io/tx/0xf708d306de39c422472f43cb975d97b66fd5d6a6863db627067167cbf93d84d1#statechange) nous voyons que `Stockage[6]` a suivi la valeur du contrat pendant un certain temps. Pour l'instant, nous l'appellerons `Value*`. L'astérisque (\*) nous rappelle que nous ne _savons_ pas encore ce que fait cette variable, mais elle ne peut pas servir uniquement à suivre la valeur du contrat, car il n'est pas nécessaire d'utiliser le stockage, qui est très coûteux, alors que vous pouvez obtenir le solde de vos comptes en utilisant `ADDRESS BALANCE`. Le premier opcode pousse la propre adresse du contrat. Le second lit l'adresse en haut de la pile et la remplace par le solde de cette adresse. -| Décalage | Opcode | Pile | -| --------:| ------------ | --------------------------------------------- | +| Décalage | Opcode | Base | +| -------: | ------------ | ------------------------------------------- | | 6C | PUSH2 0x0075 | 0x75 Value\* CALLVALUE 0 6 CALLVALUE | | 6F | SWAP2 | CALLVALUE Value\* 0x75 0 6 CALLVALUE | | 70 | SWAP1 | Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 71 | PUSH2 0x01a7 | 0x01A7 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 74 | JUMP | | +| 74 | JUMP | | Nous continuerons à tracer ce code à la destination du saut. -| Décalage | Opcode | Pile | -| --------:| ---------- | ------------------------------------------------------------- | +| Décalage | Opcode | Base | +| -------: | ---------- | ----------------------------------------------------------- | | 1A7 | JUMPDEST | Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1A8 | PUSH1 0x00 | 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1AA | DUP3 | CALLVALUE 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1AB | NOT | 2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -Le `NOT` est au niveau des bits donc il inverse la valeur de chaque bit dans la valeur d'appel. +Le `NOT` est un opérateur au niveau du bit, donc il inverse la valeur de chaque bit dans la valeur d'appel. -| Décalage | Opcode | Pile | -| --------:| ------------ | ------------------------------------------------------------------------------- | -| 1AC | DUP3 | Value\* 2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1AD | GT | Value\*>2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1AE | ISZERO | Value\*\<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1AF | PUSH2 0x01df | 0x01DF Value\*\<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | -| 1B2 | JUMPI | | +| Décalage | Opcode | Base | +| -------: | ------------ | ---------------------------------------------------------------------------------------------------- | +| 1AC | DUP3 | Value\* 2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AD | GT | Value\*>2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AE | ISZERO | Value\*<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1AF | PUSH2 0x01df | 0x01DF Value\*<=2^256-CALLVALUE-1 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | +| 1B2 | JUMPI | | -On saute si `Value*` est inférieure à 2^256-CALLVALUE-1 ou égale à celle-ci. Cela ressemble à une logique pour éviter les dépassements. Et en effet, nous voyons qu'après quelques opérations absurdes (écrire en mémoire est sur le point d'être supprimé, par exemple) au décalage 0x01DE, le contrat annule si le dépassement est détecté, ce qui est le comportement normal. +On saute si `Value*` est plus petit ou égal à 2^256-CALLVALUE-1. Cela ressemble à une logique pour éviter les dépassements. Et en effet, nous voyons qu'après quelques opérations absurdes (par exemple, écrire dans la mémoire qui est sur le point d'être effacée), au décalage 0x01DE, le contrat est annulé si le dépassement est détecté, ce qui est un comportement normal. -Notez qu'un tel dépassement est extrêmement improbable, parce qu'il nécessiterait une valeur d'appel et `Value*` d'être d'un ordre de grandeur de 2^256 wei, environ 10^59 ETH. [L'offre d'ETH total, au moment ou l'on écrit ceci, est inférieur à deux cents millions](https://etherscan.io/stat/supply). +Notez qu'un tel dépassement est extrêmement improbable, car il faudrait que la valeur d'appel plus `Value*` soit comparable à 2^256 wei, soit environ 10^59 ETH. [L'offre totale d'ETH, au moment de la rédaction de cet article, est inférieure à deux cents millions](https://etherscan.io/stat/supply). -| Décalage | Opcode | Pile | -| --------:| -------- | ------------------------------------------- | +| Décalage | Opcode | Base | +| -------: | -------- | ----------------------------------------- | | 1DF | JUMPDEST | 0x00 Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1E0 | POP | Value\* CALLVALUE 0x75 0 6 CALLVALUE | | 1E1 | ADD | Value\*+CALLVALUE 0x75 0 6 CALLVALUE | | 1E2 | SWAP1 | 0x75 Value\*+CALLVALUE 0 6 CALLVALUE | -| 1E3 | JUMP | | +| 1E3 | JUMP | | -Si nous sommes arrivés ici, obtenons `Value* + CALLVALUE` et sautons au décalage 0x75. +Si nous sommes arrivés ici, obtenez `Value* + CALLVALUE` et sautez au décalage 0x75. -| Décalage | Opcode | Pile | -| --------:| -------- | --------------------------------- | +| Décalage | Opcode | Base | +| -------: | -------- | ------------------------------- | | 75 | JUMPDEST | Value\*+CALLVALUE 0 6 CALLVALUE | | 76 | SWAP1 | 0 Value\*+CALLVALUE 6 CALLVALUE | | 77 | SWAP2 | 6 Value\*+CALLVALUE 0 CALLVALUE | -| 78 | SSTORE | 0 CALLVALUE | +| 78 | SSTORE | 0 CALLVALUE | -Si nous arrivons ici (ce qui nécessite que les données d'appel soient vides), nous ajoutons à `Value*` la valeur d'appel. Ceci est cohérent avec ce que nous disons sur ce que les transactions `Transfer` font. +Si nous arrivons ici (ce qui nécessite que les données d'appel soient vides), nous ajoutons à `Value*` la valeur d'appel. Ceci est cohérent avec ce que nous disons que les transactions de `Transfer` font. | Décalage | Opcode | -| --------:| ------ | +| -------: | ------ | | 79 | POP | | 7A | POP | | 7B | STOP | -Enfin, effacez la pile (qui n'est pas nécessaire) et signalez la fin réussie de la transaction. +Enfin, videz la pile (ce qui n'est pas nécessaire) et signalez la fin réussie de la transaction. -Pour tout résumer, voici un diagramme du code initial. +Pour résumer, voici un organigramme du code initial. -![Organigramme des points d'entrée](flowchart-entry.png) +![Organigramme du point d'entrée](flowchart-entry.png) ## Le gestionnaire à 0x7C {#the-handler-at-0x7c} -J'ai sciemment omis de mettre dans la rubrique ce que fait ce gestionnaire. Le but n'est pas de vous enseigner comment fonctionne ce contrat spécifique, mais comment rétro-concevoir les contrats. Vous apprendrez ce qu'il fait de la même manière que moi, en suivant le code. +Je n'ai volontairement pas indiqué dans le titre ce que fait ce gestionnaire. L'objectif n'est pas de vous apprendre le fonctionnement de ce contrat spécifique, mais la manière de faire de l'ingénierie inverse sur des contrats. Vous apprendrez ce qu'il fait de la même manière que moi, en suivant le code. -Nous arrivons ici de plusieurs façons : +Nous arrivons ici depuis plusieurs endroits : -- S'il y a des données d'appel de 1, 2 ou 3 octets (à partir du décalage 0x63) -- Si la signature de la méthode est inconnue (à partir des décalages 0x42 et 0x5D) +- S'il y a des données d'appel de 1, 2 ou 3 octets (depuis le décalage 0x63) +- Si la signature de la méthode est inconnue (depuis les décalages 0x42 et 0x5D) -| Décalage | Opcode | Pile | -| --------:| ------------ | -------------------- | -| 7C | JUMPDEST | | -| 7D | PUSH1 0x00 | 0x00 | -| 7F | PUSH2 0x009d | 0x9D 0x00 | -| 82 | PUSH1 0x03 | 0x03 0x9D 0x00 | -| 84 | SLOAD | Storage[3] 0x9D 0x00 | +| Décalage | Opcode | Base | +| -------: | ------------ | ------------------------------------------------------------------------- | +| 7C | JUMPDEST | | +| 7D | PUSH1 0x00 | 0x00 | +| 7F | PUSH2 0x009d | 0x9D 0x00 | +| 82 | PUSH1 0x03 | 0x03 0x9D 0x00 | +| 84 | SLOAD | Stockage[3] 0x9D 0x00 | -Il s'agit d'une autre cellule de stockage, une cellule que je n'ai pas trouvée dans aucune transaction, donc il est plus difficile de savoir ce que cela signifie. Le code ci-dessous clarifiera la question. +C'est une autre cellule de stockage, que je n'ai pu trouver dans aucune transaction, il est donc plus difficile de savoir ce qu'elle signifie. Le code ci-dessous clarifiera cela. -| Décalage | Opcode | Pile | -| --------:| ------------------------------------------------- | ------------------------------- | -| 85 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xff....ff Storage[3] 0x9D 0x00 | -| 9A | AND | Storage[3]-as-address 0x9D 0x00 | +| Décalage | Opcode | Base | +| -------: | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| 85 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xff....ff Stockage[3] 0x9D 0x00 | +| 9A | AND | Stockage[3](en tant qu'adresse) 0x9D 0x00 | -Ces codes d'opération tronquent la valeur que nous lisons de Stockage[3] à 160 bits, la longueur d'une adresse Ethereum. +Ces opcodes tronquent la valeur que nous lisons de Stockage[3] à 160 bits, la longueur d'une adresse Ethereum. -| Décalage | Opcode | Pile | -| --------:| ------ | ------------------------------- | -| 9B | SWAP1 | 0x9D Storage[3]-as-address 0x00 | -| 9C | JUMP | Storage[3]-as-address 0x00 | +| Décalage | Opcode | Base | +| -------: | ------ | ----------------------------------------------------------------------------------------------------------------- | +| 9B | SWAP1 | 0x9D Stockage[3](en tant qu'adresse) 0x00 | +| 9C | JUMP | Stockage[3](en tant qu'adresse) 0x00 | -Ce saut est superflu puisque nous allons au prochain code d'opérations. Ce code n'est pas aussi efficace en gaz qu'il pourrait l'être. +Ce saut est superflu, puisque nous allons à l'opcode suivant. Ce code n'est pas aussi économe en gaz qu'il pourrait l'être. -| Décalage | Opcode | Pile | -| --------:| ---------- | ------------------------------- | -| 9D | JUMPDEST | Storage[3]-as-address 0x00 | -| 9E | SWAP1 | 0x00 Storage[3]-as-address | -| 9F | POP | Storage[3]-as-address | -| A0 | PUSH1 0x40 | 0x40 Storage[3]-as-address | -| A2 | MLOAD | Mem[0x40] Storage[3]-as-address | +| Décalage | Opcode | Base | +| -------: | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 9D | JUMPDEST | Stockage[3](en tant qu'adresse) 0x00 | +| 9E | SWAP1 | 0x00 Stockage[3](en tant qu'adresse) | +| 9F | POP | Stockage[3](en tant qu'adresse) | +| A0 | PUSH1 0x40 | 0x40 Stockage[3](en tant qu'adresse) | +| A2 | MLOAD | Mem[0x40] Stockage[3](en tant qu'adresse) | -Au tout début du code, nous définissons Mem[0x40] à 0x80. Si nous cherchons 0x40 plus tard, nous voyons que nous ne le changeons pas - nous pouvons donc supposer qu'il est 0x80. +Au tout début du code, nous avons défini Mem[0x40] sur 0x80. Si nous cherchons 0x40 plus tard, nous voyons que nous ne le changeons pas ; nous pouvons donc supposer qu'il s'agit de 0x80. -| Décalage | Opcode | Pile | -| --------:| ------------ | ------------------------------------------------- | -| A3 | CALLDATASIZE | CALLDATASIZE 0x80 Storage[3]-as-address | -| A4 | PUSH1 0x00 | 0x00 CALLDATASIZE 0x80 Storage[3]-as-address | -| A6 | DUP3 | 0x80 0x00 CALLDATASIZE 0x80 Storage[3]-as-address | -| A7 | CALLDATACOPY | 0x80 Storage[3]-as-address | +| Décalage | Opcode | Base | +| -------: | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| A3 | CALLDATASIZE | CALLDATASIZE 0x80 Stockage[3](en tant qu'adresse) | +| A4 | PUSH1 0x00 | 0x00 CALLDATASIZE 0x80 Stockage[3](en tant qu'adresse) | +| A6 | DUP3 | 0x80 0x00 CALLDATASIZE 0x80 Stockage[3](en tant qu'adresse) | +| A7 | CALLDATACOPY | 0x80 Stockage[3](en tant qu'adresse) | Copiez toutes les données d'appel en mémoire, à partir de 0x80. -| Décalage | Opcode | Pile | -| --------:| ------------- | -------------------------------------------------------------------------------- | -| A8 | PUSH1 0x00 | 0x00 0x80 Storage[3]-as-address | -| AA | DUP1 | 0x00 0x00 0x80 Storage[3]-as-address | -| AB | CALLDATASIZE | CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AC | DUP4 | 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AD | DUP6 | Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AE | GAS | GAS Storage[3]-as-address 0x80 CALLDATASIZE 0x00 0x00 0x80 Storage[3]-as-address | -| AF | DELEGATE_CALL | | +| Décalage | Opcode | Base | +| -------: | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| A8 | PUSH1 0x00 | 0x00 0x80 Stockage[3](en tant qu'adresse) | +| AA | DUP1 | 0x00 0x00 0x80 Stockage[3](en tant qu'adresse) | +| AB | CALLDATASIZE | CALLDATASIZE 0x00 0x00 0x80 Stockage[3](en tant qu'adresse) | +| AC | DUP4 | 0x80 CALLDATASIZE 0x00 0x00 0x80 Stockage[3](en tant qu'adresse) | +| AD | DUP6 | Stockage[3](en tant qu'adresse) 0x80 CALLDATASIZE 0x00 0x00 0x80 Stockage[3](en tant qu'adresse) | +| AE | GAS | GAS Stockage[3](en tant qu'adresse) 0x80 CALLDATASIZE 0x00 0x00 0x80 Stockage[3](en tant qu'adresse) | +| AF | DELEGATE_CALL | | -Les choses sont maintenant beaucoup plus claires. Ce contrat peut agir comme un [proxy](https://blog.openzeppelin.com/proxy-patterns/), en appelant l'adresse dans Stockage[3] pour faire le travail réel. `DELEGATE_CALL` appelle un contrat séparé, mais reste dans le même stockage. Cela signifie que le contrat délégué, celui pour lequel nous sommes un proxy, accède au même espace de stockage. Les paramètres d'appel sont : +Les choses sont maintenant beaucoup plus claires. Ce contrat peut agir comme un [proxy](https://blog.openzeppelin.com/proxy-patterns/), en appelant l'adresse dans Stockage[3] pour faire le vrai travail. `DELEGATE_CALL` appelle un contrat séparé, mais reste dans le même stockage. Cela signifie que le contrat délégué, celui pour lequel nous sommes un proxy, accède au même espace de stockage. Les paramètres de l'appel sont : - _Gaz_ : Tout le gaz restant -- _Adresse appelée_ : Stockage[3] comme adresse -- _Données d'appel_ : Les octets CALLDATASIZE commençant à 0x80, où nous avons mis les données d'appel d'origine -- _Données de retour_ : Aucune (0x00 - 0x00), nous allons obtenir les données de retour par d'autres moyens (voir ci-dessous) - -| Décalage | Opcode | Pile | -| --------:| -------------- | --------------------------------------------------------------------------------------------- | -| B0 | RETURNDATASIZE | RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B1 | DUP1 | RETURNDATASIZE RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B2 | PUSH1 0x00 | 0x00 RETURNDATASIZE RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B4 | DUP5 | 0x80 0x00 RETURNDATASIZE RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B5 | RETURNDATACOPY | RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | - -Ici, nous copions toutes les données retournées dans le tampon de mémoire à partir de 0x80. - -| Décalage | Opcode | Pile | -| --------:| ------------ | ---------------------------------------------------------------------------------------------------------------------------- | -| B6 | DUP2 | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B7 | DUP1 | (((call success/failure))) (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B8 | ISZERO | (((did the call fail))) (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| B9 | PUSH2 0x00c0 | 0xC0 (((did the call fail))) (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BC | JUMPI | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BD | DUP2 | RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BE | DUP5 | 0x80 RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| BF | RETOUR | | - -Donc, après l'appel, nous copions les données retournées dans le tampon 0x80 - 0x80+RETURNDATASIZE, et si l'appel réussit, nous retournons `RETURN` avec exactement ce tampon. +- _Adresse appelée_ : Stockage[3](en tant qu'adresse) +- _Données d'appel_ : Les octets CALLDATASIZE commençant à 0x80, là où nous avons mis les données d'appel d'origine +- _Données de retour_ : Aucune (0x00 - 0x00). Nous obtiendrons les données de retour par d'autres moyens (voir ci-dessous). + +| Décalage | Opcode | Base | +| -------: | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B0 | RETURNDATASIZE | RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B1 | DUP1 | RETURNDATASIZE RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B2 | PUSH1 0x00 | 0x00 RETURNDATASIZE RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B4 | DUP5 | 0x80 0x00 RETURNDATASIZE RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B5 | RETURNDATACOPY | RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | + +Ici, nous copions toutes les données de retour dans le tampon mémoire à partir de 0x80. + +| Décalage | Opcode | Base | +| -------: | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| B6 | DUP2 | (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B7 | DUP1 | (((succès/échec de l'appel))) (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B8 | ISZERO | (((l'appel a-t-il échoué))) (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| B9 | PUSH2 0x00c0 | 0xC0 (((l'appel a-t-il échoué))) (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| BC | JUMPI | (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| BD | DUP2 | RETURNDATASIZE (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| BE | DUP5 | 0x80 RETURNDATASIZE (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| BF | RETOUR | | + +Ainsi, après l'appel, nous copions les données de retour dans le tampon 0x80 - 0x80+RETURNDATASIZE, et si l'appel réussit, nous exécutons `RETURN` avec exactement ce tampon. ### Échec de DELEGATECALL {#delegatecall-failed} -Si nous arrivons ici, à 0xC0, cela signifie que le contrat que nous avons appelé a annulé son exécution. Étant donné que nous ne sommes qu'un proxy pour ce contrat, nous voulons retourner les mêmes données et annuler également. +Si nous arrivons ici, à 0xC0, cela signifie que le contrat que nous avons appelé a été annulé. Comme nous ne sommes qu'un proxy pour ce contrat, nous voulons retourner les mêmes données et également annuler. -| Décalage | Opcode | Pile | -| --------:| -------- | ------------------------------------------------------------------------------------------------------------------- | -| C0 | JUMPDEST | (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| C1 | DUP2 | RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| C2 | DUP5 | 0x80 RETURNDATASIZE (((call success/failure))) RETURNDATASIZE (((call success/failure))) 0x80 Storage[3]-as-address | -| C3 | REVERT | | +| Décalage | Opcode | Base | +| -------: | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| C0 | JUMPDEST | (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| C1 | DUP2 | RETURNDATASIZE (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| C2 | DUP5 | 0x80 RETURNDATASIZE (((succès/échec de l'appel))) RETURNDATASIZE (((succès/échec de l'appel))) 0x80 Stockage[3](en tant qu'adresse) | +| C3 | REVERT | | -Donc nous annulons `REVERT` avec le même tampon que nous avons utilisé pour `RETURN` précédemment : 0x80 - 0x80+RETURNDATASIZE +Nous exécutons donc `REVERT` avec le même tampon que nous avons utilisé pour `RETURN` précédemment : 0x80 - 0x80+RETURNDATASIZE -![Organigramme de l'appel de proxy](flowchart-proxy.png) +![Organigramme de l'appel au proxy](flowchart-proxy.png) ## Appels ABI {#abi-calls} -Si la taille des données de l'appel est de quatre octets ou plus, il peut s'agir d'un appel ABI valide. +Si la taille des données d'appel est de quatre octets ou plus, il peut s'agir d'un appel ABI valide. -| Décalage | Opcode | Pile | -| --------:| ------------ | ------------------------------------------------- | -| D | PUSH1 0x00 | 0x00 | -| F | CALLDATALOAD | (((First word (256 bits) of the call data))) | -| 10 | PUSH1 0xe0 | 0xE0 (((First word (256 bits) of the call data))) | -| 12 | SHR | (((first 32 bits (4 bytes) of the call data))) | +| Décalage | Opcode | Base | +| -------: | ------------ | --------------------------------------------------------------------------------------------------------------------------------- | +| D | PUSH1 0x00 | 0x00 | +| F | CALLDATALOAD | (((Premier mot (256 bits) des données d'appel))) | +| 10 | PUSH1 0xe0 | 0xE0 (((Premier mot (256 bits) des données d'appel))) | +| 12 | SHR | (((premiers 32 bits (4 octets) des données d'appel))) | -Etherscan nous dit que `1C` est un code d'opération inconnu, parce qu'[il a été ajouté après que Etherscan ait écrit cette fonctionnalité](https://eips.ethereum.org/EIPS/eip-145) et qu'ils ne l'ont pas mise à jour. Un [tableau d'opcode à jour](https://github.com/wolflo/evm-opcodes) nous montre que c'est un décalage à droite +Etherscan nous dit que `1C` est un opcode inconnu, car [il a été ajouté après qu'Etherscan ait écrit cette fonctionnalité](https://eips.ethereum.org/EIPS/eip-145) et ils ne l'ont pas mis à jour. Une [table d'opcodes à jour](https://github.com/wolflo/evm-opcodes) nous montre qu'il s'agit d'un décalage à droite -| Décalage | Opcode | Pile | -| --------:| ---------------- | -------------------------------------------------------------------------------------------------------- | -| 13 | DUP1 | (((first 32 bits (4 bytes) of the call data))) (((first 32 bits (4 bytes) of the call data))) | -| 14 | PUSH4 0x3cd8045e | 0x3CD8045E (((first 32 bits (4 bytes) of the call data))) (((first 32 bits (4 bytes) of the call data))) | -| 19 | GT | 0x3CD8045E>first-32-bits-of-the-call-data (((first 32 bits (4 bytes) of the call data))) | -| 1A | PUSH2 0x0043 | 0x43 0x3CD8045E>first-32-bits-of-the-call-data (((first 32 bits (4 bytes) of the call data))) | -| 1D | JUMPI | (((first 32 bits (4 bytes) of the call data))) | +| Décalage | Opcode | Base | +| -------: | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 13 | DUP1 | (((premiers 32 bits (4 octets) des données d'appel))) (((premiers 32 bits (4 octets) des données d'appel))) | +| 14 | PUSH4 0x3cd8045e | 0x3CD8045E (((premiers 32 bits (4 octets) des données d'appel))) (((premiers 32 bits (4 octets) des données d'appel))) | +| 19 | GT | 0x3CD8045E > premiers 32 bits des données d'appel (((premiers 32 bits (4 octets) des données d'appel))) | +| 1A | PUSH2 0x0043 | 0x43 0x3CD8045E > premiers 32 bits des données d'appel (((premiers 32 bits (4 octets) des données d'appel))) | +| 1D | JUMPI | (((premiers 32 bits (4 octets) des données d'appel))) | -Diviser les tests de correspondance de la signature de la méthode en deux comme cela réduit les tests de moitié en moyenne. Le code qui suit immédiatement cela et le code en 0x43 suivent le même modèle : `DUP1` les 32 premiers bits des données d'appel, `PUSH4 (((méthode signature>`, exécutez `EQ` pour vérifier l'égalité, puis `JUMPI` si la signature de la méthode correspond. Voici les signatures de la méthode, leurs adresses, et si elles sont connues [la définition de méthode correspondante](https://www.4byte.directory/) : +Diviser ainsi les tests de correspondance de signature de méthode en deux permet de réduire de moitié le nombre moyen de tests. Le code qui suit immédiatement et le code en 0x43 suivent le même modèle : `DUP1` les 32 premiers bits des données d'appel, `PUSH4 (((signature de méthode>`, exécutez `EQ` pour vérifier l'égalité, puis `JUMPI` si la signature de méthode correspond. Voici les signatures de méthode, leurs adresses et, si elle est connue, [la définition de méthode correspondante](https://www.4byte.directory/) : -| Méthode | Signature de la méthode | Décalage vers lequel sauter | -| -------------------------------------------------------------------------------------- | ----------------------- | --------------------------- | -| [splitter()](https://www.4byte.directory/signatures/?bytes4_signature=0x3cd8045e) | 0x3cd8045e | 0x0103 | -| ??? | 0x81e580d3 | 0x0138 | -| [currentWindow()](https://www.4byte.directory/signatures/?bytes4_signature=0xba0bafb4) | 0xba0bafb4 | 0x0158 | -| ??? | 0x1f135823 | 0x00C4 | -| [merkleRoot()](https://www.4byte.directory/signatures/?bytes4_signature=0x2eb4a7ab) | 0x2eb4a7ab | 0x00ED | +| Méthode | Signature de la méthode | Décalage de destination du saut | +| --------------------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------- | +| [splitter()](https://www.4byte.directory/signatures/?bytes4_signature=0x3cd8045e) | 0x3cd8045e | 0x0103 | +| ??? | 0x81e580d3 | 0x0138 | +| [currentWindow()](https://www.4byte.directory/signatures/?bytes4_signature=0xba0bafb4) | 0xba0bafb4 | 0x0158 | +| ??? | 0x1f135823 | 0x00C4 | +| [merkleRoot()](https://www.4byte.directory/signatures/?bytes4_signature=0x2eb4a7ab) | 0x2eb4a7ab | 0x00ED | -Si aucune correspondance n'est trouvée, le code saute vers [le gestionnaire de proxy à 0x7C](#the-handler-at-0x7c), dans l'espoir que le contrat pour lequel nous sommes proxy ait une correspondance. +Si aucune correspondance n'est trouvée, le code saute vers [le gestionnaire de proxy à 0x7C](#the-handler-at-0x7c), dans l'espoir que le contrat pour lequel nous sommes un proxy ait une correspondance. ![Organigramme des appels ABI](flowchart-abi.png) ## splitter() {#splitter} -| Décalage | Opcode | Pile | -| --------:| ------------ | ----------------------------- | +| Décalage | Opcode | Base | +| -------: | ------------ | ----------------------------- | | 103 | JUMPDEST | | | 104 | CALLVALUE | CALLVALUE | | 105 | DUP1 | CALLVALUE CALLVALUE | @@ -314,27 +312,27 @@ Si aucune correspondance n'est trouvée, le code saute vers [le gestionnaire de | 10D | DUP1 | 0x00 0x00 CALLVALUE | | 10E | REVERT | | -La première chose que fait cette fonction est de vérifier que l'appel n'a pas envoyé d'ETH. Cette fonction n'est pas [`payable`](https://solidity-by-example.org/payable/). Si quelqu'un nous a envoyé des ETH, cela doit être une erreur et nous voulons `REVERT` pour éviter d'avoir cet ETH où il ne peut le récupérer. - -| Décalage | Opcode | Pile | -| --------:| ------------------------------------------------- | --------------------------------------------------------------------------- | -| 10F | JUMPDEST | | -| 110 | POP | | -| 111 | PUSH1 0x03 | 0x03 | -| 113 | SLOAD | (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 114 | PUSH1 0x40 | 0x40 (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 116 | MLOAD | 0x80 (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 117 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xFF...FF 0x80 (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 12C | SWAP1 | 0x80 0xFF...FF (((Storage[3] a.k.a the contract for which we are a proxy))) | -| 12D | SWAP2 | (((Storage[3] a.k.a the contract for which we are a proxy))) 0xFF...FF 0x80 | -| 12E | AND | ProxyAddr 0x80 | -| 12F | DUP2 | 0x80 ProxyAddr 0x80 | -| 130 | MSTORE | 0x80 | +La première chose que fait cette fonction est de vérifier que l'appel n'a envoyé aucun ETH. Cette fonction n'est pas [`payable`](https://solidity-by-example.org/payable/). Si quelqu'un nous a envoyé des ETH, cela doit être une erreur et nous voulons exécuter `REVERT` pour éviter que ces ETH ne soient bloqués là où ils ne peuvent pas être récupérés. + +| Décalage | Opcode | Base | +| -------: | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 10F | JUMPDEST | | +| 110 | POP | | +| 111 | PUSH1 0x03 | 0x03 | +| 113 | SLOAD | (((Stockage[3] alias le contrat pour lequel nous sommes un proxy))) | +| 114 | PUSH1 0x40 | 0x40 (((Stockage[3] alias le contrat pour lequel nous sommes un proxy))) | +| 116 | MLOAD | 0x80 (((Stockage[3] alias le contrat pour lequel nous sommes un proxy))) | +| 117 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff | 0xFF...FF 0x80 (((Stockage[3] alias le contrat pour lequel nous sommes un proxy))) | +| 12C | SWAP1 | 0x80 0xFF...FF (((Stockage[3] alias le contrat pour lequel nous sommes un proxy))) | +| 12D | SWAP2 | (((Stockage[3] alias le contrat pour lequel nous sommes un proxy))) 0xFF...FF 0x80 | +| 12E | AND | ProxyAddr 0x80 | +| 12F | DUP2 | 0x80 ProxyAddr 0x80 | +| 130 | MSTORE | 0x80 | Et 0x80 contient maintenant l'adresse du proxy -| Décalage | Opcode | Pile | -| --------:| ------------ | --------- | +| Décalage | Opcode | Base | +| -------: | ------------ | --------- | | 131 | PUSH1 0x20 | 0x20 0x80 | | 133 | ADD | 0xA0 | | 134 | PUSH2 0x00e4 | 0xE4 0xA0 | @@ -342,10 +340,10 @@ Et 0x80 contient maintenant l'adresse du proxy ### Le code E4 {#the-e4-code} -C'est la première fois que nous voyons ces lignes, mais elles sont partagées avec d'autres méthodes (voir ci-dessous). Nous allons donc appeler la valeur dans la pile X, et n'oubliez pas que dans la fonction `splitter()` la valeur de ce X est 0xA0. +C'est la première fois que nous voyons ces lignes, mais elles sont partagées avec d'autres méthodes (voir ci-dessous). Nous appellerons donc la valeur dans la pile X, et nous nous souviendrons simplement que dans `splitter()`, la valeur de ce X est 0xA0. -| Décalage | Opcode | Pile | -| --------:| ---------- | ----------- | +| Décalage | Opcode | Base | +| -------: | ---------- | ----------- | | E4 | JUMPDEST | X | | E5 | PUSH1 0x40 | 0x40 X | | E7 | MLOAD | 0x80 X | @@ -355,30 +353,30 @@ C'est la première fois que nous voyons ces lignes, mais elles sont partagées a | EB | SWAP1 | 0x80 X-0x80 | | EC | RETOUR | | -Ce code reçoit un pointeur de mémoire dans la pile (X), et entraîne le contrat à `RETURN` avec un tampon qui est 0x80 - X. +Ce code reçoit donc un pointeur de mémoire dans la pile (X) et fait en sorte que le contrat exécute `RETURN` avec un tampon qui est 0x80 - X. -Dans le cas de `splitter()`, ceci retourne l'adresse pour laquelle nous sommes un proxy. `RETURN` retourne le tampon en 0x80-0x9F, où nous avons écrit ces données (décalage 0x130 ci-dessus). +Dans le cas de `splitter()`, ceci retourne l'adresse pour laquelle nous sommes un proxy. `RETURN` renvoie le tampon dans 0x80-0x9F, où nous avons écrit ces données (décalage 0x130 ci-dessus). ## currentWindow() {#currentwindow} -Le code aux décalages 0x158-0x163 est identique à ce que nous avons vu en 0x103-0x10E dans `splitter()` (autre que la destination `JUMPI`), donc nous savons que `currentWindow()` n'est pas `payable` non plus. +Le code aux décalages 0x158-0x163 est identique à ce que nous avons vu en 0x103-0x10E dans `splitter()` (autre que la destination `JUMPI`), donc nous savons que `currentWindow()` n'est pas non plus `payable`. -| Décalage | Opcode | Pile | -| --------:| ------------ | -------------------- | -| 164 | JUMPDEST | | -| 165 | POP | | -| 166 | PUSH2 0x00da | 0xDA | -| 169 | PUSH1 0x01 | 0x01 0xDA | -| 16B | SLOAD | Storage[1] 0xDA | -| 16C | DUP2 | 0xDA Storage[1] 0xDA | -| 16D | JUMP | Storage[1] 0xDA | +| Décalage | Opcode | Base | +| -------: | ------------ | ------------------------------------------------------------------------- | +| 164 | JUMPDEST | | +| 165 | POP | | +| 166 | PUSH2 0x00da | 0xDA | +| 169 | PUSH1 0x01 | 0x01 0xDA | +| 16B | SLOAD | Stockage[1] 0xDA | +| 16C | DUP2 | 0xDA Stockage[1] 0xDA | +| 16D | JUMP | Stockage[1] 0xDA | ### Le code DA {#the-da-code} -Ce code est aussi partagé avec d'autres méthodes. Nous allons donc appeler la valeur dans la pile Y, et n'oubliez pas que dans la fonction `currentWindow()` la valeur de ce Y est Stockage[1]. +Ce code est aussi partagé avec d'autres méthodes. Nous allons donc appeler la valeur dans la pile Y, et nous souvenir que dans `currentWindow()` la valeur de ce Y est Stockage[1]. -| Décalage | Opcode | Pile | -| --------:| ---------- | ---------------- | +| Décalage | Opcode | Base | +| -------: | ---------- | ---------------- | | DA | JUMPDEST | Y 0xDA | | DB | PUSH1 0x40 | 0x40 Y 0xDA | | DD | MLOAD | 0x80 Y 0xDA | @@ -388,357 +386,359 @@ Ce code est aussi partagé avec d'autres méthodes. Nous allons donc appeler la Écrire Y à 0x80-0x9F. -| Décalage | Opcode | Pile | -| --------:| ---------- | -------------- | +| Décalage | Opcode | Base | +| -------: | ---------- | -------------- | | E1 | PUSH1 0x20 | 0x20 0x80 0xDA | | E3 | ADD | 0xA0 0xDA | -Et le reste est déjà expliqué [au-dessus](#the-e4-code). Donc les sauts à 0xDA écrivent la pile supérieure (Y) à 0x80-0x9F, et retournent cette valeur. Dans le cas de `currentWindow()`, il retourne Stockage[1]. +Et le reste est déjà expliqué [ci-dessus](#the-e4-code). Les sauts vers 0xDA écrivent donc la valeur supérieure de la pile (Y) à 0x80-0x9F, et renvoient cette valeur. Dans le cas de `currentWindow()`, il retourne Stockage[1]. ## merkleRoot() {#merkleroot} -Le code aux décalages 0xED-0xF8 est identique à ce que nous avons vu en 0x103-0x10E dans `splitter()` (autre que la destination `JUMPI`), donc nous savons que `merkleRoot()` n'est pas `payable` non plus. +Le code aux décalages 0xED-0xF8 est identique à ce que nous avons vu en 0x103-0x10E dans `splitter()` (autre que la destination `JUMPI`), donc nous savons que `merkleRoot()` n'est pas non plus `payable`. -| Décalage | Opcode | Pile | -| --------:| ------------ | -------------------- | -| F9 | JUMPDEST | | -| FA | POP | | -| FB | PUSH2 0x00da | 0xDA | -| FE | PUSH1 0x00 | 0x00 0xDA | -| 100 | SLOAD | Storage[0] 0xDA | -| 101 | DUP2 | 0xDA Storage[0] 0xDA | -| 102 | JUMP | Storage[0] 0xDA | +| Décalage | Opcode | Base | +| -------: | ------------ | ------------------------------------------------------------------------- | +| F9 | JUMPDEST | | +| FA | POP | | +| FB | PUSH2 0x00da | 0xDA | +| FE | PUSH1 0x00 | 0x00 0xDA | +| 100 | SLOAD | Stockage[0] 0xDA | +| 101 | DUP2 | 0xDA Stockage[0] 0xDA | +| 102 | JUMP | Stockage[0] 0xDA | -[Nous avons déjà compris](#the-da-code) ce qu'il se passe après le saut. Donc `merkleRoot()` retourne Stockage[0]. +Nous avons déjà compris ce qu'il se passe après le saut ([voir plus haut](#the-da-code)). Donc `merkleRoot()` retourne Stockage[0]. ## 0x81e580d3 {#0x81e580d3} -Le code aux décalages 0x138-0x143 est identique à ce que nous avons vu en 0x103-0x10E dans `splitter()` (autre que la destination `JUMPI`), donc nous savons que cette fonction n'est pas `payable` non plus. - -| Décalage | Opcode | Pile | -| --------:| ------------ | ------------------------------------------------------------ | -| 144 | JUMPDEST | | -| 145 | POP | | -| 146 | PUSH2 0x00da | 0xDA | -| 149 | PUSH2 0x0153 | 0x0153 0xDA | -| 14C | CALLDATASIZE | CALLDATASIZE 0x0153 0xDA | -| 14D | PUSH1 0x04 | 0x04 CALLDATASIZE 0x0153 0xDA | -| 14F | PUSH2 0x018f | 0x018F 0x04 CALLDATASIZE 0x0153 0xDA | -| 152 | JUMP | 0x04 CALLDATASIZE 0x0153 0xDA | -| 18F | JUMPDEST | 0x04 CALLDATASIZE 0x0153 0xDA | -| 190 | PUSH1 0x00 | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 192 | PUSH1 0x20 | 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 194 | DUP3 | 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 195 | DUP5 | CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 196 | SUB | CALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 197 | SLT | CALLDATASIZE-4\<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 198 | ISZERO | CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 199 | PUSH2 0x01a0 | 0x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 19C | JUMPI | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +Le code aux décalages 0x138-0x143 est identique à ce que nous avons vu en 0x103-0x10E dans `splitter()` (autre que la destination `JUMPI`), donc nous savons que cette fonction n'est pas non plus `payable`. + +| Décalage | Opcode | Base | +| -------: | ------------ | ----------------------------------------------------------------------------- | +| 144 | JUMPDEST | | +| 145 | POP | | +| 146 | PUSH2 0x00da | 0xDA | +| 149 | PUSH2 0x0153 | 0x0153 0xDA | +| 14C | CALLDATASIZE | CALLDATASIZE 0x0153 0xDA | +| 14D | PUSH1 0x04 | 0x04 CALLDATASIZE 0x0153 0xDA | +| 14F | PUSH2 0x018f | 0x018F 0x04 CALLDATASIZE 0x0153 0xDA | +| 152 | JUMP | 0x04 CALLDATASIZE 0x0153 0xDA | +| 18F | JUMPDEST | 0x04 CALLDATASIZE 0x0153 0xDA | +| 190 | PUSH1 0x00 | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 192 | PUSH1 0x20 | 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 194 | DUP3 | 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 195 | DUP5 | CALLDATASIZE 0x04 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 196 | SUB | CALLDATASIZE-4 0x20 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 197 | SLT | CALLDATASIZE-4<32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 198 | ISZERO | CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 199 | PUSH2 0x01a0 | 0x01A0 CALLDATASIZE-4>=32 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 19C | JUMPI | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | Il semblerait que cette fonction prenne au moins 32 octets (un mot) de données d'appel. -| Décalage | Opcode | Pile | -| --------:| ------ | -------------------------------------------- | +| Décalage | Opcode | Base | +| -------: | ------ | -------------------------------------------- | | 19D | DUP1 | 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | | 19E | DUP2 | 0x00 0x00 0x00 0x04 CALLDATASIZE 0x0153 0xDA | | 19F | REVERT | | -Si elle ne récupère pas les données d'appel, la transaction est annulée sans aucune donnée retournée. +Si elle n'obtient pas les données d'appel, la transaction est annulée sans aucune donnée de retour. Voyons ce qui se passe si la fonction _obtient_ les données d'appel dont elle a besoin. -| Décalage | Opcode | Pile | -| --------:| ------------ | ---------------------------------------- | -| 1A0 | JUMPDEST | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | -| 1A1 | POP | 0x04 CALLDATASIZE 0x0153 0xDA | +| Décalage | Opcode | Base | +| -------: | ------------ | ----------------------------------------------------------- | +| 1A0 | JUMPDEST | 0x00 0x04 CALLDATASIZE 0x0153 0xDA | +| 1A1 | POP | 0x04 CALLDATASIZE 0x0153 0xDA | | 1A2 | CALLDATALOAD | calldataload(4) CALLDATASIZE 0x0153 0xDA | `calldataload(4)` est le premier mot des données d'appel _après_ la signature de la méthode -| Décalage | Opcode | Pile | -| --------:| ------------ | ---------------------------------------------------------------------------- | -| 1A3 | SWAP2 | 0x0153 CALLDATASIZE calldataload(4) 0xDA | -| 1A4 | SWAP1 | CALLDATASIZE 0x0153 calldataload(4) 0xDA | -| 1A5 | POP | 0x0153 calldataload(4) 0xDA | -| 1A6 | JUMP | calldataload(4) 0xDA | -| 153 | JUMPDEST | calldataload(4) 0xDA | -| 154 | PUSH2 0x016e | 0x016E calldataload(4) 0xDA | -| 157 | JUMP | calldataload(4) 0xDA | -| 16E | JUMPDEST | calldataload(4) 0xDA | -| 16F | PUSH1 0x04 | 0x04 calldataload(4) 0xDA | -| 171 | DUP2 | calldataload(4) 0x04 calldataload(4) 0xDA | -| 172 | DUP2 | 0x04 calldataload(4) 0x04 calldataload(4) 0xDA | -| 173 | SLOAD | Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA | -| 174 | DUP2 | calldataload(4) Storage[4] calldataload(4) 0x04 calldataload(4) 0xDA | -| 175 | LT | calldataload(4)\)`, et une autre est `isClaimed()`, donc cela ressemble à un contrat d'airdrop. Au lieu de fouiller le code d'opération restant par opcode, nous pouvons [essayer le décompilateur](https://etherscan.io/bytecode-decompiler?a=0x2f81e57ff4f4d83b40a9f719fd892d8e806e0761), qui donne des résultats utilisables pour trois fonctions de ce contrat. La rétro-conception des autres est laissée au lecteur comme exercice de travail. +L'une des méthodes restantes est `claim()` et une autre est `isClaimed()`, donc cela ressemble à un contrat d'airdrop. Une des méthodes restantes est `claim()`, et une autre est `isClaimed()`, donc cela ressemble à un contrat d'airdrop. Au lieu de passer en revue le reste des opcodes un par un, nous pouvons [essayer le décompilateur](https://etherscan.io/bytecode-decompiler?a=0x2f81e57ff4f4d83b40a9f719fd892d8e806e0761), qui produit des résultats utilisables pour trois fonctions de ce contrat. -### scaleAmountByPercentage {#scaleamountbypercentage} +### L'ingénierie inverse des autres est laissée comme exercice au lecteur. -Voilà ce que nous donne le décompilateur pour cette fonction : +scaleAmountByPercentage {#scaleamountbypercentage} ```python -def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable: - require calldata.size - 4 >=′ 64 - if _param1 and _param2 > -1 / _param1: - revert with 0, 17 - return (_param1 * _param2 / 100 * 10^6) +Voici ce que le décompilateur nous donne pour cette fonction : ``` -Le premier `require` vérifie que les données d'appel ont, en plus des quatre octets de la signature de la fonction, au moins 64 octets, assez pour les deux paramètres. Si ce n'est pas le cas, il y a évidemment quelque chose qui ne va pas. +def unknown8ffb5c97(uint256 _param1, uint256 _param2) payable: +require calldata.size - 4 >=′ 64 +if _param1 and _param2 > -1 / _param1: +revert with 0, 17 +return (_param1 \* _param2 / 100 \* 10^6) Le premier `require` teste si les données d'appel contiennent, en plus des quatre octets de la signature de fonction, au moins 64 octets, ce qui est suffisant pour les deux paramètres. -L'instruction `if` semble vérifier que `_param1` n'est pas zéro et que `_param1 * _param2` n'est pas négatif. C'est probablement pour éviter des cas de renvoi à la ligne. +Sinon, il y a manifestement un problème. L'instruction `if` semble vérifier que `_param1` n'est pas nul et que `_param1 * _param2` n'est pas négatif. -Enfin, la fonction retourne une valeur mise à l'échelle. +C'est probablement pour éviter les cas de bouclage (wrap around). -### claim {#claim} +### Enfin, la fonction retourne une valeur mise à l'échelle. -Le code que le décompilateur crée est complexe, et tout n'est pas pertinent pour nous. Je vais en passer une partie pour me concentrer sur les lignes qui je pense fournissent des informations utiles +claim {#claim} Le code que le décompilateur crée est complexe, et tout n'est pas pertinent pour nous. ```python -def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable: - ... - require _param2 == addr(_param2) - ... - if currentWindow <= _param1: - revert with 0, 'cannot claim for a future window' +Je vais en sauter une partie pour me concentrer sur les lignes qui, à mon avis, fournissent des informations utiles. ``` -Nous voyons ici deux choses importantes : +def unknown2e7ba6ef(uint256 _param1, uint256 _param2, uint256 _param3, array _param4) payable: +... +require _param2 == addr(_param2) +... +if currentWindow <= _param1: +revert with 0, 'cannot claim for a future window' +- Nous voyons ici deux choses importantes : - `_param2`, bien qu'il soit déclaré comme un `uint256`, est en fait une adresse -- `_param1` est la fenêtre revendiquée, qui doit être `currentWindow` ou antérieure. ```python - ... - if stor5[_claimWindow][addr(_claimFor)]: - revert with 0, 'Account already claimed the given window' +`_param1` est la fenêtre réclamée, qui doit être `currentWindow` ou une fenêtre antérieure. ``` -Nous savons donc maintenant que Stockage[5] est un tableau de fenêtres et d'adresses, et si l'adresse a réclamé la récompense pour cette fenêtre. +... +if stor5[_claimWindow][addr(_claimFor)]: +revert with 0, 'Account already claimed the given window' ```python - ... - idx = 0 - s = 0 - while idx < _param4.length: - ... - if s + sha3(mem[(32 * _param4.length) + 328 len mem[(32 * _param4.length) + 296]]) > mem[(32 * idx) + 296]: - mem[mem[64] + 32] = mem[(32 * idx) + 296] - ... - s = sha3(mem[_62 + 32 len mem[_62]]) - continue - ... - s = sha3(mem[_66 + 32 len mem[_66]]) - continue - if unknown2eb4a7ab != s: - revert with 0, 'Invalid proof' +Nous savons donc maintenant que Stockage[5] est un tableau de fenêtres et d'adresses, et qu'il indique si l'adresse a réclamé la récompense pour cette fenêtre. ``` -Nous savons que `unknown2eb4a7ab` est en fait la fonction `merkleRoot()`, donc ce code semble vérifier une [preuve de Merkle](https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5). Cela signifie que `_param4` est une preuve de Merkle. +... +idx = 0 +s = 0 +while idx < _param4.length: +... +if s + sha3(mem[(32 \* _param4.length) + 328 len mem[(32 \* _param4.length) + 296]]) > mem[(32 \* idx) + 296]: +mem[mem[64] + 32] = mem[(32 \* idx) + 296] +... +s = sha3(mem[_62 + 32 len mem[_62]]) +continue +... +s = sha3(mem[_66 + 32 len mem[_66]]) +continue +if unknown2eb4a7ab != s: +revert with 0, 'Invalid proof' Nous savons que `unknown2eb4a7ab` est en fait la fonction `merkleRoot()`, ce code semble donc vérifier une [preuve de Merkle](https://medium.com/crypto-0-nite/merkle-proofs-explained-6dd429623dc5). ```python - call addr(_param2) with: - value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei - gas 30000 wei +Cela signifie que `_param4` est une preuve de Merkle. ``` -C’est ainsi qu’un contrat transfère son propre ETH à une autre adresse (contrat ou propriété externe). Il l'appelle avec une valeur qui est le montant à transférer. On dirait donc qu'il s'agit d'un airdrop d'ETH. +call addr(_param2) with: +value unknown81e580d3[_param1] \* _param3 / 100 \* 10^6 wei +gas 30000 wei C'est ainsi qu'un contrat transfère ses propres ETH à une autre adresse (contrat ou compte externe). Il l'appelle avec une valeur qui est le montant à transférer. ```python - if not return_data.size: - if not ext_call.success: - require ext_code.size(stor2) - call stor2.deposit() with: - value unknown81e580d3[_param1] * _param3 / 100 * 10^6 wei +Il semble donc qu'il s'agisse d'un airdrop d'ETH. ``` -Les deux dernières lignes nous disent que Stockage[2] est également un contrat que nous appelons. Si nous [regardons la transaction constructeur](https://etherscan.io/tx/0xa1ea0549fb349eb7d3aff90e1d6ce7469fdfdcd59a2fd9b8d1f5e420c0d05b58#statechange) nous voyons que ce contrat est [0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2), un contrat de Wrapped Ether[dont le code source a été téléchargé sur Etherscan](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code). +if not return_data.size: +if not ext_call.success: +require ext_code.size(stor2) +call stor2.deposit() with: +value unknown81e580d3[_param1] \* _param3 / 100 \* 10^6 wei Les deux dernières lignes nous disent que Stockage[2] est aussi un contrat que nous appelons. -Il semble donc que les contrats tentent d'envoyer de l'ETH à `_param2`. S'ils peuvent le faire, très bien. Sinon, il tente d'envoyer [WETH](https://weth.tkn.eth.limo/). Si `_param2` est un compte externe (EOA) alors il peut toujours recevoir de l'ETH, mais les contrats peuvent refuser de recevoir de l'ETH. Cependant, WETH est ERC-20 et les contrats ne peuvent pas refuser de l'accepter. +Si nous [examinons la transaction du constructeur](https://etherscan.io/tx/0xa1ea0549fb349eb7d3aff90e1d6ce7469fdfdcd59a2fd9b8d1f5e420c0d05b58#statechange), nous voyons que ce contrat est [0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2), un contrat Wrapped Ether [dont le code source a été téléversé sur Etherscan](https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code). Il semble donc que les contrats tentent d'envoyer des ETH à `_param2`. S'il peut le faire, tant mieux. Sinon, il tente d'envoyer du [WETH](https://weth.tkn.eth.limo/). Si `_param2` est un compte externe (EOA), il peut toujours recevoir des ETH, mais les contrats peuvent refuser de recevoir des ETH. ```python - ... - log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] * _param3 / 100 * 10^6, bool(ext_call.success) +Cependant, le WETH est un ERC-20 et les contrats ne peuvent pas refuser de l'accepter. ``` -À la fin de la fonction, nous voyons qu'une entrée de journal est générée. [Regardez les entrées de journal générées](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#events) et filtrez sur le sujet qui commence par `0xdbd5...`. Si nous [cliquons sur l'une des transactions qui ont généré une telle entrée](https://etherscan.io/tx/0xe7d3b7e00f645af17dfbbd010478ef4af235896c65b6548def1fe95b3b7d2274) nous voyons qu'effectivement cela ressemble à une demande - le compte a envoyé un message au contrat que nous rétro-concevons et a reçu de l'ETH en retour. +... +log 0xdbd5389f: addr(_param2), unknown81e580d3[_param1] \* _param3 / 100 \* 10^6, bool(ext_call.success) À la fin de la fonction, nous voyons qu'une entrée de journal est générée. [Examinez les entrées de journal générées](https://etherscan.io/address/0x2510c039cc3b061d79e564b38836da87e31b342f#events) et filtrez sur le sujet qui commence par `0xdbd5...`. -![Une transaction de réclamation](claim-tx.png) +Si nous [cliquons sur l'une des transactions qui ont généré une telle entrée](https://etherscan.io/tx/0xe7d3b7e00f645af17dfbbd010478ef4af235896c65b6548def1fe95b3b7d2274), nous voyons qu'il s'agit bien d'une réclamation : le compte a envoyé un message au contrat sur lequel nous faisons de l'ingénierie inverse et a reçu de l'ETH en retour. -### 1e7df9d3 {#1e7df9d3} +### ![Une transaction de réclamation](claim-tx.png) -Cette fonction est très similaire à [`claim`](#claim) ci-dessus. Elle vérifie également une preuve de Merkle, tente de transférer de l'ETH au premier, et produit le même type d'entrée de journal. +1e7df9d3 {#1e7df9d3} Cette fonction est très similaire à [`claim`](#claim) ci-dessus. ```python -def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable: - ... - idx = 0 - s = 0 - while idx < _param3.length: - if idx >= mem[96]: - revert with 0, 50 - _55 = mem[(32 * idx) + 128] - if s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) > mem[(32 * idx) + 128]: - ... - s = sha3(mem[_58 + 32 len mem[_58]]) - continue - mem[mem[64] + 32] = s + sha3(mem[(32 * _param3.length) + 160 len mem[(32 * _param3.length) + 128]]) - ... - if unknown2eb4a7ab != s: - revert with 0, 'Invalid proof' - ... - call addr(_param1) with: - value s wei - gas 30000 wei - if not return_data.size: - if not ext_call.success: - require ext_code.size(stor2) - call stor2.deposit() with: - value s wei - gas gas_remaining wei - ... - log 0xdbd5389f: addr(_param1), s, bool(ext_call.success) +Elle vérifie également une preuve de Merkle, tente de transférer de l'ETH à la première et produit le même type d'entrée de journal. ``` -La principale différence est que le premier paramètre, la fenêtre du retrait, n'est pas là. Au lieu de cela, il y a une boucle sur toutes les fenêtres qui pourraient être réclamées. +def unknown1e7df9d3(uint256 _param1, uint256 _param2, array _param3) payable: +... +idx = 0 +s = 0 +while idx < _param3.length: +if idx >= mem[96]: +revert with 0, 50 +_55 = mem[(32 \* idx) + 128] +if s + sha3(mem[(32 \* _param3.length) + 160 len mem[(32 \* _param3.length) + 128]]) > mem[(32 \* idx) + 128]: +... +s = sha3(mem[_58 + 32 len mem[_58]]) +continue +mem[mem[64] + 32] = s + sha3(mem[(32 \* _param3.length) + 160 len mem[(32 \* _param3.length) + 128]]) +... +if unknown2eb4a7ab != s: +revert with 0, 'Invalid proof' +... +call addr(_param1) with: +value s wei +gas 30000 wei +if not return_data.size: +if not ext_call.success: +require ext_code.size(stor2) +call stor2.deposit() with: +value s wei +gas gas_remaining wei +... +log 0xdbd5389f: addr(_param1), s, bool(ext_call.success) La principale différence est que le premier paramètre, la fenêtre à retirer, n'est pas là. ```python - idx = 0 - s = 0 - while idx < currentWindow: - ... - if stor5[mem[0]]: - if idx == -1: - revert with 0, 17 - idx = idx + 1 - s = s - continue - ... - stor5[idx][addr(_param1)] = 1 - if idx >= unknown81e580d3.length: - revert with 0, 50 - mem[0] = 4 - if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]: - revert with 0, 17 - if s > !(unknown81e580d3[idx] * _param2 / 100 * 10^6): - revert with 0, 17 - if idx == -1: - revert with 0, 17 - idx = idx + 1 - s = s + (unknown81e580d3[idx] * _param2 / 100 * 10^6) - continue +À la place, il y a une boucle sur toutes les fenêtres qui pourraient être réclamées. ``` -Donc elle ressemble à une variante de `claim` qui réclame toutes les fenêtres. +idx = 0 +s = 0 +while idx < currentWindow: +... +if stor5[mem[0]]: +if idx == -1: +revert with 0, 17 +idx = idx + 1 +s = s +continue +... +stor5[idx][addr(_param1)] = 1 +if idx >= unknown81e580d3.length: +revert with 0, 50 +mem[0] = 4 +if unknown81e580d3[idx] and _param2 > -1 / unknown81e580d3[idx]: +revert with 0, 17 +if s > !(unknown81e580d3[idx] \* _param2 / 100 \* 10^6): +revert with 0, 17 +if idx == -1: +revert with 0, 17 +idx = idx + 1 +s = s + (unknown81e580d3[idx] \* _param2 / 100 \* 10^6) +continue ## Conclusion {#conclusion} -À présent, vous devriez savoir comment comprendre les contrats dont le code source n'est pas disponible, en utilisant soit les codes d'opérations soit (quand cela fonctionne) le décompilateur. Comme le montre la longueur de cet article, rétro-concevoir un contrat n'est pas trivial, mais dans un système où la sécurité est essentielle, il est important d'être capable de vérifier que les contrats fonctionnent comme promis. +Cela ressemble donc à une variante de `claim` qui réclame toutes les fenêtres. À présent, vous devriez savoir comment comprendre les contrats dont le code source n'est pas disponible, en utilisant soit les opcodes, soit (quand cela fonctionne) le décompilateur. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/run-node-raspberry-pi/index.md b/public/content/translations/fr/developers/tutorials/run-node-raspberry-pi/index.md index 0024da7a636..c1b54cb4c3b 100644 --- a/public/content/translations/fr/developers/tutorials/run-node-raspberry-pi/index.md +++ b/public/content/translations/fr/developers/tutorials/run-node-raspberry-pi/index.md @@ -1,47 +1,49 @@ --- -title: Comment transformer son Raspberry Pi 4 en un nœud en flashant simplement la carte MicroSD -description: Flashez votre Raspberry Pi 4, branchez-y un câble ethernet, connectez le disque SSD et mettez l'appareil en marche pour transformer votre Raspberry Pi 4 en un nœud Ethereum complet + validateur +title: "Exécuter un nœud Ethereum sur un Raspberry Pi 4" +description: "Flashez votre Raspberry Pi 4, branchez un câble Ethernet, connectez le disque SSD et mettez l'appareil sous tension pour le transformer en un nœud et validateur Ethereum complet" author: "EthereumOnArm" tags: - - "clients" - - "couche d'exécution" - - "couche de consensus" - - "nœuds" + [ + "clients", + "couche d'exécution", + "couche de consensus", + "nœuds" + ] lang: fr skill: intermediate published: 2022-06-10 -source: Ethereum sur ARM +source: Ethereum on ARM sourceUrl: https://ethereum-on-arm-documentation.readthedocs.io/en/latest/ --- -**Ethereum sur Arm est une image Linux personnalisée qui peut transformer un Raspberry Pi en un nœud Ethereum.** +**Ethereum on Arm est une image Linux personnalisée qui peut transformer un Raspberry Pi en un nœud Ethereum.** -Pour utiliser Ethereum sur Arm pour transformer un Raspberry Pi en un nœud Ethereum, le matériel suivant est recommandé : +Pour utiliser Ethereum on Arm afin de transformer un Raspberry Pi en nœud Ethereum, le matériel suivant est recommandé : -- Carte Raspberry 4 (modèle B 8Go), Odroid M1 ou Rock 5B (8Go/16Go de RAM) -- Carte MicroSD (16 Go Classe 10 minimum) -- Disque 2 To SSD USB 3.0 minimum ou un SSD avec USB vers un SATA. +- Carte Raspberry 4 (modèle B 8 Go), Odroid M1 ou Rock 5B (8 Go/16 Go de RAM) +- Carte microSD (16 Go, classe 10 au minimum) +- Disque SSD de 2 To minimum avec port USB 3.0 ou un SSD avec un boîtier USB vers SATA. - Alimentation électrique -- Un câble Ethernet -- Transfert de port (voir clients pour plus d'informations) -- Un boîtier avec dissipateur de chaleur et ventilateur -- Clavier USB, moniteur et câble HDMI (micro-HDMI) (facultatif) +- Câble Ethernet +- Redirection de port (voir les clients pour plus d'informations) +- Un boîtier avec dissipateur thermique et ventilateur +- Clavier USB, moniteur et câble HDMI (micro-HDMI) (en option) -## Pourquoi utiliser Ethereum avec ARM ? {#why-run-ethereum-on-arm} +## Pourquoi faire tourner Ethereum sur ARM ? {#why-run-ethereum-on-arm} -Les cartes ARM sont très abordables, flexibles et équivalentes à de petits ordinateurs. Elles sont de bons choix pour faire fonctionner des nœuds Ethereum car elles sont bon marché, configurées de sorte que toutes leurs ressources se concentrent uniquement sur le nœud, les rendant ainsi efficaces, elles consomment peu de puissance et sont physiquement peu nombreuses pour qu'elles puissent s'adapter discrètement à n'importe quel environnement. Il est également très facile de faire tourner des nœuds puisque la MicroSD du Raspberry Pi peut simplement être flashée avec une image reconstruite, sans téléchargement ou logiciel de construction requis. +Les cartes ARM sont de petits ordinateurs très abordables et flexibles. Elles sont un bon choix pour exécuter des nœuds Ethereum, car elles sont bon marché, peuvent être configurées de manière à ce que toutes leurs ressources se concentrent sur le nœud (ce qui les rend efficaces), consomment peu d'énergie et sont de petite taille, ce qui leur permet de s'intégrer discrètement dans n'importe quel foyer. Il est également très facile de lancer des nœuds, car la carte MicroSD du Raspberry Pi peut simplement être flashée avec une image pré-construite, sans qu'il soit nécessaire de télécharger ou de compiler des logiciels. ## Comment ça marche ? {#how-does-it-work} -La carte mémoire du Raspberry Pi est flashée avec une image préconstruite. Cette image contient tout ce qui est nécessaire pour exécuter un nœud Ethereum. Avec une carte flash, tout ce que l'utilisateur a besoin de faire est d'allumer son Raspberry Pi. Tous les processus requis pour exécuter le nœud sont démarrés automatiquement. Cela fonctionne parce que la carte mémoire contient un système d'exploitation (OS) basé sur Linux sur lequel sont exécutés automatiquement les processus au niveau du système qui transforment l'unité en un nœud Ethereum. +La carte mémoire du Raspberry Pi est flashée avec une image pré-construite. Cette image contient tout ce qui est nécessaire pour exécuter un nœud Ethereum. Avec une carte flashée, il suffit à l'utilisateur d'allumer le Raspberry Pi. Tous les processus nécessaires pour exécuter le nœud sont démarrés automatiquement. Cela fonctionne, car la carte mémoire contient un système d'exploitation (SE) basé sur Linux, sur lequel des processus au niveau du système sont exécutés automatiquement pour transformer l'appareil en un nœud Ethereum. -Ethereum ne peut pas être exécuté en utilisant le populaire Raspberry Pi Linux OS « Raspbian » car Raspbian utilise toujours une architecture 32 bits qui conduit les utilisateurs d'Ethereum à rencontrer des problèmes de mémoire et les clients de consensus ne prennent pas en charge les binaires 32 bits. Pour surmonter cela, l'équipe Ethereum on Arm a migré vers un OS 64 bits natif appelé « Armbian ». +Ethereum ne peut pas être exécuté avec le système d'exploitation populaire du Raspberry Pi, « Raspbian », car ce dernier utilise toujours une architecture 32 bits, ce qui cause des problèmes de mémoire aux utilisateurs d'Ethereum, et les clients de consensus ne prennent pas en charge les binaires 32 bits. Pour surmonter cela, l'équipe Ethereum on Arm a migré vers un système d'exploitation natif 64 bits appelé « Armbian ». -**Les images s'occupent de réaliser toutes les étapes nécessaires**, allant de la configuration de l'environnement et du formatage du disque SSD, à l'installation et à l'exécution du logiciel Ethereum, ainsi qu'au lancement de la synchronisation avec la blockchain. +**Les images s'occupent de toutes les étapes nécessaires**, de la configuration de l'environnement et du formatage du disque SSD à l'installation et l'exécution du logiciel Ethereum, ainsi qu'au démarrage de la synchronisation de la blockchain. -## Note sur les clients d'exécution et de consensus {#note-on-execution-and-consensus-clients} +## Remarque sur les clients d'exécution et de consensus {#note-on-execution-and-consensus-clients} -L'image Ethereum sur Arm inclut les clients d'exécution et de consensuel préconstruits en tant que services. Un nœud Ethereum nécessite que les deux clients soient synchronisés et exécutés. Vous devez seulement télécharger et flasher l'image et ensuite démarrer les services. L'image est préchargée avec les clients d'exécution suivants : +L'image Ethereum on Arm inclut des clients d'exécution et de consensus pré-construits en tant que services. Un nœud Ethereum nécessite que les deux clients soient synchronisés et en cours d'exécution. Il vous suffit de télécharger et de flasher l'image, puis de démarrer les services. L'image est préchargée avec les clients d'exécution suivants : - Geth - Nethermind @@ -54,84 +56,84 @@ et les clients de consensus suivants : - Prysm - Teku -Vous devez choisir un de chaque à exécuter - tous les clients d'exécution sont compatibles avec tous les clients de consensus. Si vous ne sélectionnez pas explicitement un client, le noeud va revenir à ses valeurs par défaut - Geth et Lighthouse - et les exécuter automatiquement lorsque la carte sera mise en marche. Vous devez ouvrir le port 30303 sur votre routeur pour que Geth puisse trouver et se connecter aux pairs. +Vous devez en choisir un de chaque à exécuter. Tous les clients d'exécution sont compatibles avec tous les clients de consensus. Si vous ne sélectionnez pas explicitement un client, le nœud utilisera ses options par défaut (Geth et Lighthouse) et les exécutera automatiquement à la mise sous tension de la carte. Vous devez ouvrir le port 30303 sur votre routeur pour que Geth puisse trouver des pairs et s'y connecter. ## Téléchargement de l'image {#downloading-the-image} -L'image Ethereum Raspberry Pi 4 est une image « plug and play » qui installe et configure automatiquement à la fois les clients d'exécution et de consensus pour communiquer mutuellement et se connecter au réseau Ethereum. Tout ce que l'utilisateur doit faire est de démarrer ses processus en utilisant une commande simple. +L'image Ethereum pour Raspberry Pi 4 est une image « prête à l'emploi » qui installe et configure automatiquement les clients d'exécution et de consensus, en les configurant pour qu'ils communiquent entre eux et se connectent au réseau Ethereum. L'utilisateur n'a qu'à démarrer les processus à l'aide d'une simple commande. -Téléchargez l'image Raspberry Pi depuis [Ethereum sur Arm](https://ethereumonarm-my.sharepoint.com/:u:/p/dlosada/Ec_VmUvr80VFjf3RYSU-NzkBmj2JOteDECj8Bibde929Gw?download=1) et vérifiez le hachage SHA256 : +Téléchargez l'image du Raspberry Pi depuis [Ethereum on Arm](https://ethereumonarm-my.sharepoint.com/:u:/p/dlosada/Ec_VmUvr80VFjf3RYSU-NzkBmj2JOteDECj8Bibde929Gw?download=1) et vérifiez le hachage SHA256 : ```sh -# From directory containing the downloaded image +# À partir du répertoire contenant l'image téléchargée shasum -a 256 ethonarm_22.04.00.img.zip -# Hash should output: fb497e8f8a7388b62d6e1efbc406b9558bee7ef46ec7e53083630029c117444f +# Le hachage devrait être : fb497e8f8a7388b62d6e1efbc406b9558bee7ef46ec7e53083630029c117444f ``` -Notez que les images des cartes Rock 5B et Odroid M1 sont disponibles sur la page de téléchargement [Ethereum sur Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/quick-guide/download-and-install.html). +Notez que les images pour les cartes Rock 5B et Odroid M1 sont disponibles sur la [page de téléchargement](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/quick-guide/download-and-install.html) d'Ethereum-on-Arm. ## Flasher la carte MicroSD {#flashing-the-microsd} -La carte MicroSD qui sera utilisée pour le Raspberry Pi doit d'abord être insérée dans un ordinateur de bureau ou portable pour qu'elle puisse être flashée. Ensuite, les commandes de terminal suivantes installeront l'image téléchargée sur la carte SD : +La carte MicroSD qui sera utilisée pour le Raspberry Pi doit d'abord être insérée dans un ordinateur de bureau ou un ordinateur portable afin de pouvoir être flashée. Ensuite, les commandes de terminal suivantes flasheront l'image téléchargée sur la carte SD : ```shell -# check the MicroSD card name +# vérifiez le nom de la carte MicroSD sudo fdisk -l >> sdxxx ``` -Il est vraiment important d'obtenir le nom correct, car la commande suivante inclut `dd` qui efface complètement le contenu existant de la carte avant de flasher l'image dessus. Pour continuer, accédez au répertoire contenant l'image compressée : +Il est très important d'utiliser le bon nom, car la commande suivante inclut `dd`, qui efface complètement le contenu de la carte avant d'y copier l'image. Pour continuer, accédez au répertoire contenant l'image compressée : ```shell -# unzip and flash image +# décompressez et flashez l'image unzip ethonarm_22.04.00.img.zip sudo dd bs=1M if=ethonarm_22.04.00.img of=/dev/ conv=fdatasync status=progress ``` -La carte est maintenant flashée et peut être insérée dans le Raspberry Pi. +La carte est maintenant flashée, elle peut donc être insérée dans le Raspberry Pi. ## Démarrer le nœud {#start-the-node} -Avec la carte SD insérée dans le Raspberry Pi, connectez le câble ethernet et le SSD puis allumez l'alimentation. L'OS démarrera et commencera automatiquement à exécuter les tâches préconfigurées qui transforment le Raspberry Pi en un nœud Ethereum, y compris l'installation et la construction du logiciel client. Cela prendra probablement 10 à 15 minutes. +Une fois la carte SD insérée dans le Raspberry Pi, branchez le câble Ethernet et le SSD, puis mettez l'appareil sous tension. Le système d'exploitation démarrera et commencera automatiquement à exécuter les tâches préconfigurées qui transforment le Raspberry Pi en un nœud Ethereum, y compris l'installation et la compilation du logiciel client. Cela prendra probablement 10 à 15 minutes. -Une fois que tout est installé et configuré, connectez-vous au périphérique via une connexion ssh ou directement en utilisant le terminal si un moniteur et un clavier sont connectés à la carte. Utilisez le compte `ethereum` pour vous connecter, car il a les permissions requises pour démarrer le nœud. +Une fois que tout est installé et configuré, connectez-vous à l'appareil via une connexion SSH ou en utilisant directement le terminal si un moniteur et un clavier sont connectés à la carte. Utilisez le compte `ethereum` pour vous connecter, car il dispose des autorisations requises pour démarrer le nœud. ```shell Utilisateur : ethereum Mot de passe : ethereum ``` -Le client d'exécution par défaut, Geth, démarrera automatiquement. Vous pouvez confirmer cela en vérifiant les logs en utilisant la ligne de commande suivante : +Le client d'exécution par défaut, Geth, démarrera automatiquement. Vous pouvez le confirmer en vérifiant les journaux à l'aide de la commande suivante : ```sh sudo journalctl -u geth -f ``` -Le client de consensus doit être démarré explicitement. Pour ce faire, ouvrez d'abord le port 9000 sur votre routeur afin que Lighthouse puisse trouver des paires et s'y connecter. Ensuite, activez et démarrez le service Lighthouse : +Le client de consensus doit être démarré explicitement. Pour ce faire, ouvrez d'abord le port 9000 sur votre routeur afin que Lighthouse puisse trouver des pairs et s'y connecter. Ensuite, activez et démarrez le service Lighthouse : ```sh sudo systemctl enable lighthouse-beacon sudo systemctl start lighthouse-beacon ``` -Vérifiez le client en utilisant les logs : +Vérifiez le client à l'aide des journaux : ```sh sudo journalctl -u lighthouse-beacon ``` -Notez que le client de consensus se synchronisera après quelques minutes car il utilise la synchronisation de point de contrôle. Le client d'exécution prendra plus de temps - potentiellement plusieurs heures, et il ne démarrera pas jusqu'à ce que le client de consensus soit déjà synchronisé (du fait que le client d'exécution a besoin d'une cible pour synchroniser, que le client de consensus synchronisé fournit). +Notez que le client de consensus se synchronisera en quelques minutes, car il utilise la synchronisation par point de contrôle. Le client d'exécution mettra plus de temps (potentiellement plusieurs heures) et ne démarrera pas tant que le client de consensus n'aura pas terminé sa synchronisation (car le client d'exécution a besoin d'une cible avec laquelle se synchroniser, cible qui est fournie par le client de consensus synchronisé). -Avec les services Geth et Lighthouse exécutant et synchronisés, votre Raspberry Pi est maintenant un nœud Ethereum ! Il est plus courant d'interagir avec le réseau Ethereum en utilisant la console JavaScript de Geth, qui peut être reliée au client Geth sur le port 8545. Il est également possible de soumettre des commandes formatées en objets JSON en utilisant un outil de requête tel que Curl. En savoir plus dans la [documentation Geth](https://geth.ethereum.org). +Une fois les services Geth et Lighthouse en cours d'exécution et synchronisés, votre Raspberry Pi est désormais un nœud Ethereum complet ! L'interaction avec le réseau Ethereum se fait le plus souvent via la console Javascript de Geth, qui peut être attachée au client Geth sur le port 8545. Il est également possible de soumettre des commandes formatées en tant qu'objets JSON à l'aide d'un outil de requête tel que Curl. Pour en savoir plus, consultez la [documentation de Geth](https://geth.ethereum.org/). -Geth est préconfiguré pour rapporter des mesures sur un tableau de bord Grafana qui peut être consulté dans le navigateur. Les utilisateurs plus avancés pourraient vouloir utiliser cette fonctionnalité pour surveiller la santé de leur nœud en naviguant vers `ipaddress:3000`, utilisant `utilisateur : admin` et `passe: ethereum`. +Geth est préconfiguré pour envoyer des métriques à un tableau de bord Grafana qui peut être consulté dans le navigateur. Les utilisateurs plus avancés peuvent utiliser cette fonctionnalité pour surveiller la santé de leur nœud en se rendant sur `ipaddress:3000`, et en utilisant `user: admin` et `passwd: ethereum`. ## Validateurs {#validators} -Un validateur peut également être ajouté au client de consensus. Le logiciel du validateur permet à votre nœud de participer activement au consensus et fournit au réseau une sécurité cryptoéconomique. Vous avez été récompensé pour ce travail en ETH. Pour faire fonctionner un validateur, vous devez d'abord avoir 32 ETH, qui doivent être déposés dans le contrat de dépôt. **Ceci est un engagement à long terme - il n'est pas encore possible de retirer cet ETH !**. Le dépôt peut être fait en suivant le guide étape par étape sur la [plateforme de lancement](https://launchpad.ethereum.org/). Faites ceci sur un ordinateur de bureau ou portable, mais ne générez pas de clés — cela peut être fait directement sur le Raspberry Pi. +Un validateur peut également être ajouté en option au client de consensus. Le logiciel de validation permet à votre nœud de participer activement au consensus et de fournir au réseau une sécurité cryptoéconomique. Vous êtes récompensé pour ce travail en ETH. Pour exécuter un validateur, vous devez d'abord disposer de 32 ETH, qui doivent être déposés dans le contrat de dépôt. Le dépôt peut être effectué en suivant le guide étape par étape sur le [Launchpad](https://launchpad.ethereum.org/). Faites-le sur un ordinateur de bureau/portable, mais ne générez pas les clés – cette opération peut être effectuée directement sur le Raspberry Pi. -Ouvrir un terminal sur le Raspberry Pi et exécutez la commande suivante pour générer les clés de dépôt : +Ouvrez un terminal sur le Raspberry Pi et exécutez la commande suivante pour générer les clés de dépôt : ``` sudo apt-get update @@ -139,32 +141,35 @@ sudo apt-get install staking-deposit-cli cd && deposit new-mnemonic --num_validators 1 ``` -Gardez la phrase mnémonique en sécurité ! La commande ci-dessus a généré deux fichiers dans le répertoire de clés du noeud : les clés du validateur et un fichier de données de dépôt. Les données de dépôt doivent être téléchargées sur la plateforme de lancement, donc elles doivent être copiées du Raspberry Pi à l'ordinateur. Cela peut être fait en utilisant une connexion ssh ou toute autre méthode de copier/coller. +(Ou téléchargez le [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) pour l'exécuter sur une machine hors ligne, et exécutez la commande `deposit new-mnemnonic`) -Une fois que le fichier de données de dépôt est disponible sur l'ordinateur exécutant la plateforme de lancement, il peut être déplacé et déposé sur le `+` de l'écran de la plateforme de lancement. Suivez les instructions à l'écran pour envoyer une transaction au contrat de dépôt. +Conservez la phrase mnémonique en lieu sûr ! La commande ci-dessus a généré deux fichiers dans le keystore du nœud : les clés de validateur et un fichier de données de dépôt. Les données de dépôt doivent être téléversées sur le Launchpad, elles doivent donc être copiées du Raspberry Pi vers l'ordinateur de bureau/portable. Cela peut être fait en utilisant une connexion SSH ou toute autre méthode de copier-coller. -Sur le Raspberry Pi, un validateur peut être démarré. Cela nécessite d'importer les clés du validateur, de définir l'adresse pour collecter les récompenses, puis de démarrer le processus de validateur préconfiguré. L'exemple ci-dessous est pour Lighthouse : des instructions pour d'autres clients de consensus sont disponibles sur [la documentation Ethereum on Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/) : +Une fois que le fichier de données de dépôt est disponible sur l'ordinateur exécutant le Launchpad, il peut être glissé-déposé sur le `+` de l'écran du Launchpad. Suivez les instructions à l'écran pour envoyer une transaction au contrat de dépôt. + +De retour sur le Raspberry Pi, un validateur peut être démarré. Cela nécessite d'importer les clés du validateur, de définir l'adresse de collecte des récompenses, puis de démarrer le processus de validation préconfiguré. L'exemple ci-dessous concerne Lighthouse. Les instructions pour les autres clients de consensus sont disponibles dans la [documentation Ethereum on Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/) : ```shell -# import the validator keys +# importer les clés du validateur lighthouse account validator import --directory=/home/ethereum/validator_keys -# set the reward address +# définir l'adresse de récompense sudo sed -i 's/' /etc/ethereum/lighthouse-validator.conf -# start the validator +# démarrer le validateur sudo systemctl start lighthouse-validator ``` -Félicitations, vous disposez maintenant d'un nœud Ethereum complet et d'un validateur fonctionnant sur un Raspberry Pi ! +Félicitations, vous avez maintenant un nœud et un validateur Ethereum complets fonctionnant sur un Raspberry Pi ! ## Plus de détails {#more-details} -Cette page a donné un aperçu de la façon de mettre en place un nœud et un validateur Geth-Lighthouse utilisant Raspberry Pi. Des instructions plus détaillées sont disponibles sur[ le site Ethereum-on-Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/index.html). +Cette page a donné un aperçu de la configuration d'un nœud et d'un validateur Geth-Lighthouse à l'aide d'un Raspberry Pi. Des instructions plus détaillées sont disponibles sur le [site web d'Ethereum-on-Arm](https://ethereum-on-arm-documentation.readthedocs.io/en/latest/index.html). -## Commentaires appréciés {#feedback-appreciated} +## Vos commentaires sont les bienvenus {#feedback-appreciated} -Nous savons que le Raspberry Pi dispose d'une importante base d'utilisateurs qui pourrait avoir un impact très positif sur la santé du réseau Ethereum. Veuillez parcourir les détails de ce tutoriel, essayez d'exécuter sur les réseaux de test, regardez sur le GitHub Ethereum-on-Arm, émettez vos commentaires, soulevez des problématiques et des pull requests et aidez ainsi à faire avancer la technologie et la documentation ! +Nous savons que le Raspberry Pi a une base d'utilisateurs massive qui pourrait avoir un impact très positif sur la santé du réseau Ethereum. +N'hésitez pas à examiner les détails de ce tutoriel, à faire des essais sur les réseaux de test, à consulter le GitHub d'Ethereum on Arm, à donner votre avis, à signaler des problèmes et à proposer des pull requests pour aider à faire progresser la technologie et la documentation ! ## Références {#references} diff --git a/public/content/translations/fr/developers/tutorials/scam-token-tricks/index.md b/public/content/translations/fr/developers/tutorials/scam-token-tricks/index.md new file mode 100644 index 00000000000..e1e89572706 --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/scam-token-tricks/index.md @@ -0,0 +1,470 @@ +--- +title: "Quelques astuces utilisées par les jetons frauduleux et comment les détecter" +description: "Dans ce tutoriel, nous disséquons un jeton frauduleux pour voir certaines des astuces que les escrocs utilisent, comment ils les mettent en œuvre, et comment nous pouvons les détecter." +author: Ori Pomerantz +tags: + [ + "escroquerie", + "solidité", + "erc-20", + "javascript", + "typescript" + ] +skill: intermediate +published: 2023-09-15 +lang: fr +--- + +Dans ce tutoriel, nous disséquons [un jeton frauduleux](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code) pour voir certaines des astuces que les escrocs utilisent et comment ils les mettent en œuvre. À la fin de ce tutoriel, vous aurez une vue plus complète des contrats de jeton ERC-20, de leurs capacités, et de la raison pour laquelle le scepticisme est nécessaire. Ensuite, nous examinons les événements émis par ce jeton frauduleux et voyons comment nous pouvons identifier automatiquement qu'il n'est pas légitime. + +## Jetons frauduleux - que sont-ils, pourquoi les gens les créent-ils, et comment les éviter {#scam-tokens} + +Ethereum est couramment utilisé par des groupes pour créer des jetons échangeables ou, dans un certain sens, leur propre monnaie. Cependant, partout où il existe des cas d'utilisation légitimes qui apportent de la valeur, il y a aussi des criminels qui essaient de voler cette valeur à leur profit. + +Vous pouvez en lire plus sur ce sujet [ailleurs sur ethereum.org](/guides/how-to-id-scam-tokens/) du point de vue de l'utilisateur. Ce tutoriel se concentre sur la dissection d'un jeton frauduleux pour voir comment cela est fait et comment il peut être détecté. + +### Comment savoir que wARB est une escroquerie ? {#warb-scam} + +Le jeton que nous disséquons est [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code), qui prétend être équivalent au [jeton ARB](https://etherscan.io/token/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1) légitime. + +Le moyen le plus simple de savoir quel est le jeton légitime est de regarder l'organisation d'origine, [Arbitrum](https://arbitrum.foundation/). Les adresses légitimes sont spécifiées [dans leur documentation](https://docs.arbitrum.foundation/deployment-addresses#token). + +### Pourquoi le code source est-il disponible ? {#why-source} + +Normalement, on s'attendrait à ce que les gens qui essaient d'escroquer les autres soient secrets, et en effet beaucoup de jetons frauduleux n'ont pas leur code disponible (par exemple, [celui-ci](https://optimistic.etherscan.io/token/0x15992f382d8c46d667b10dc8456dc36651af1452#code) et [celui-là](https://optimistic.etherscan.io/token/0x026b623eb4aada7de37ef25256854f9235207178#code)). + +Cependant, les jetons légitimes publient généralement leur code source, donc pour paraître légitimes, les auteurs de jetons frauduleux font parfois la même chose. [wARB](https://etherscan.io/token/0xb047c8032b99841713b8e3872f06cf32beb27b82#code) est l'un de ces jetons dont le code source est disponible, ce qui facilite sa compréhension. + +Alors que les déployeurs de contrats peuvent choisir de publier ou non le code source, ils _ne peuvent pas_ publier le mauvais code source. L'explorateur de blocs compile le code source fourni de manière indépendante, et s'il n'obtient pas exactement le même bytecode, il rejette ce code source. [Vous pouvez en savoir plus à ce sujet sur le site Etherscan](https://etherscan.io/verifyContract). + +## Comparaison avec les jetons ERC-20 légitimes {#compare-legit-erc20} + +Nous allons comparer ce jeton à des jetons ERC-20 légitimes. Si vous ne savez pas comment les jetons ERC-20 légitimes sont généralement écrits, [consultez ce tutoriel](/developers/tutorials/erc20-annotated-code/). + +### Constantes pour les adresses privilégiées {#constants-for-privileged-addresses} + +Les contrats ont parfois besoin d'adresses privilégiées. Les contrats qui sont conçus pour une utilisation à long terme permettent à une adresse privilégiée de changer ces adresses, par exemple pour permettre l'utilisation d'un nouveau contrat multisig. Il y a plusieurs façons de le faire. + +Le contrat de [jeton `HOP`](https://etherscan.io/address/0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc#code) utilise le modèle [`Ownable`](https://docs.openzeppelin.com/contracts/2.x/access-control#ownership-and-ownable). L'adresse privilégiée est conservée dans le stockage, dans un champ appelé `_owner` (voir le troisième fichier, `Ownable.sol`). + +```solidity +abstract contract Ownable is Context { + address private _owner; + . + . + . +} +``` + +Le contrat de [jeton `ARB`](https://etherscan.io/address/0xad0c361ef902a7d9851ca7dcc85535da2d3c6fc7#code) n'a pas directement d'adresse privilégiée. Cependant, il n'en a pas besoin. Il se trouve derrière un [`proxy`](https://docs.openzeppelin.com/contracts/5.x/api/proxy) à l'[adresse `0xb50721bcf8d664c30412cfbc6cf7a15145234ad1`](https://etherscan.io/address/0xb50721bcf8d664c30412cfbc6cf7a15145234ad1#code). Ce contrat a une adresse privilégiée (voir le quatrième fichier, `ERC1967Upgrade.sol`) qui peut être utilisée pour les mises à niveau. + +```solidity + /** + * @dev Stocke une nouvelle adresse dans l'emplacement d'administration EIP1967. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } +``` + +En revanche, le contrat `wARB` a un `contract_owner` codé en dur. + +```solidity +contract WrappedArbitrum is Context, IERC20 { + . + . + . + address deployer = 0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1; + address public contract_owner = 0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33; + . + . + . +} +``` + +[Ce propriétaire de contrat](https://etherscan.io/address/0xb40dE7b1beE84Ff2dc22B70a049A07A13a411A33) n'est pas un contrat qui pourrait être contrôlé par différents comptes à différents moments, mais un [compte externe](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs). Cela signifie qu'il est probablement conçu pour une utilisation à court terme par un individu, plutôt que comme une solution à long terme pour contrôler un ERC-20 qui restera de valeur. + +Et en effet, si nous regardons dans Etherscan, nous voyons que l'escroc n'a utilisé ce contrat que pendant 12 heures (de la [première transaction](https://etherscan.io/tx/0xf49136198c3f925fcb401870a669d43cecb537bde36eb8b41df77f06d5f6fbc2) à la [dernière transaction](https://etherscan.io/tx/0xdfd6e717157354e64bbd5d6adf16761e5a5b3f914b1948d3545d39633244d47b)) le 19 mai 2023. + +### La fausse fonction `_transfer` {#the-fake-transfer-function} + +Il est standard que les transferts réels se produisent en utilisant [une fonction `_transfer` interne](/developers/tutorials/erc20-annotated-code/#the-_transfer-function-_transfer). + +Dans `wARB`, cette fonction semble presque légitime : + +```solidity + function _transfer(address sender, address recipient, uint256 amount) internal virtual{ + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +La partie suspecte est : + +```solidity + if (sender == contract_owner){ + sender = deployer; + } + emit Transfer(sender, recipient, amount); +``` + +Si le propriétaire du contrat envoie des jetons, pourquoi l'événement `Transfer` montre-t-il qu'ils proviennent de `deployer` ? + +Cependant, il y a un problème plus important. Qui appelle cette fonction `_transfer` ? Elle ne peut pas être appelée de l'extérieur, elle est marquée comme `internal`. Et le code que nous avons n'inclut aucun appel à `_transfer`. Clairement, il est ici comme un leurre. + +```solidity + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _f_(_msgSender(), recipient, amount); + return true; + } + + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _f_(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } +``` + +Lorsque nous examinons les fonctions qui sont appelées pour transférer des jetons, `transfer` et `transferFrom`, nous voyons qu'elles appellent une fonction complètement différente, `_f_`. + +### La vraie fonction `_f_` {#the-real-f-function} + +```solidity + function _f_(address sender, address recipient, uint256 amount) internal _mod_(sender,recipient,amount) virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + if (sender == contract_owner){ + + sender = deployer; + } + emit Transfer(sender, recipient, amount); + } +``` + +Il y a deux signaux d'alarme potentiels dans cette fonction. + +- L'utilisation du [modificateur de fonction](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `_mod_`. Cependant, quand nous examinons le code source, nous voyons que `_mod_` est en fait inoffensif. + + ```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } + ``` + +- Le même problème que nous avons vu dans `_transfer`, qui est que lorsque `contract_owner` envoie des jetons, ils semblent provenir de `deployer`. + +### La fausse fonction d'événements `dropNewTokens` {#the-fake-events-function-dropNewTokens} + +Nous arrivons maintenant à quelque chose qui ressemble à une véritable escroquerie. J'ai un peu modifié la fonction pour la lisibilité, mais elle est fonctionnellement équivalente. + +```solidity +function dropNewTokens(address uPool, + address[] memory eReceiver, + uint256[] memory eAmounts) public auth() +``` + +Cette fonction a le modificateur `auth()`, ce qui signifie qu'elle ne peut être appelée que par le propriétaire du contrat. + +```solidity +modifier auth() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; +} +``` + +Cette restriction est parfaitement logique, car nous ne voudrions pas que des comptes aléatoires distribuent des jetons. Cependant, le reste de la fonction est suspect. + +```solidity +{ + for (uint256 i = 0; i < eReceiver.length; i++) { + emit Transfer(uPool, eReceiver[i], eAmounts[i]); + } +} +``` + +Une fonction pour transférer depuis un compte de pool vers un tableau de récepteurs un tableau de montants est parfaitement logique. Il existe de nombreux cas d'utilisation dans lesquels vous voudrez distribuer des jetons d'une source unique à plusieurs destinations, comme les paies, les airdrops, etc. Il est moins cher (en gaz) de le faire en une seule transaction plutôt que d'émettre plusieurs transactions, ou même d'appeler l'ERC-20 plusieurs fois à partir d'un contrat différent dans le cadre de la même transaction. + +Cependant, `dropNewTokens` ne fait pas cela. Il émet des [événements `Transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer-1), mais ne transfère en réalité aucun jeton. Il n'y a aucune raison légitime de semer la confusion dans les applications hors chaîne en leur parlant d'un transfert qui n'a pas vraiment eu lieu. + +### La fonction `Approve` de burn {#the-burning-approve-function} + +Les contrats ERC-20 sont censés avoir [une fonction `approve`](/developers/tutorials/erc20-annotated-code/#approve) pour les allocations, et en effet notre jeton frauduleux a une telle fonction, et elle est même correcte. Cependant, comme Solidity est un dérivé du C, il est sensible à la casse. "Approve" et "approve" sont des chaînes de caractères différentes. + +De plus, la fonctionnalité n'est pas liée à `approve`. + +```solidity + function Approve( + address[] memory holders) +``` + +Cette fonction est appelée avec un tableau d'adresses pour les détenteurs du jeton. + +```solidity + public approver() { +``` + +Le modificateur `approver()` s'assure que seul `contract_owner` est autorisé à appeler cette fonction (voir ci-dessous). + +```solidity + for (uint256 i = 0; i < holders.length; i++) { + uint256 amount = _balances[holders[i]]; + _beforeTokenTransfer(holders[i], 0x0000000000000000000000000000000000000001, amount); + _balances[holders[i]] = _balances[holders[i]].sub(amount, + "ERC20: burn amount exceeds balance"); + _balances[0x0000000000000000000000000000000000000001] = + _balances[0x0000000000000000000000000000000000000001].add(amount); + } + } + +``` + +Pour chaque adresse de détenteur, la fonction déplace l'intégralité du solde du détenteur vers l'adresse `0x00...01`, le brûlant (« burn ») de fait (le `burn` réel dans la norme modifie également l'offre totale, et transfère les jetons vers `0x00...00`). Cela signifie que `contract_owner` peut supprimer les actifs de n'importe quel utilisateur. Cela ne semble pas être une fonctionnalité que vous voudriez dans un jeton de gouvernance. + +### Problèmes de qualité du code {#code-quality-issues} + +Ces problèmes de qualité du code ne _prouvent_ pas que ce code est une escroquerie, mais ils le font paraître suspect. Les entreprises organisées telles qu'Arbitrum ne publient généralement pas de code d'aussi mauvaise qualité. + +#### La fonction `mount` {#the-mount-function} + +Bien que cela ne soit pas spécifié dans [la norme](https://eips.ethereum.org/EIPS/eip-20), de manière générale, la fonction qui crée de nouveaux jetons est appelée [`mint`](https://ethereum.org/el/developers/tutorials/erc20-annotated-code/#the-_mint-and-_burn-functions-_mint-and-_burn). + +Si nous regardons dans le constructeur de `wARB`, nous voyons que la fonction de frappe a été renommée en `mount` pour une raison quelconque, et est appelée cinq fois avec un cinquième de l'offre initiale, au lieu d'une seule fois pour le montant total par souci d'efficacité. + +```solidity + constructor () public { + + _name = "Wrapped Arbitrum"; + _symbol = "wARB"; + _decimals = 18; + uint256 initialSupply = 1000000000000; + + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + mount(deployer, initialSupply*(10**18)/5); + } +``` + +La fonction `mount` elle-même est également suspecte. + +```solidity + function mount(address account, uint256 amount) public { + require(msg.sender == contract_owner, "ERC20: mint to the zero address"); +``` + +En regardant le `require`, nous voyons que seul le propriétaire du contrat est autorisé à frapper. C'est légitime. Mais le message d'erreur devrait être _seul le propriétaire est autorisé à frapper_ ou quelque chose comme ça. Au lieu de cela, c'est l'inapproprié _ERC20: mint to the zero address_. Le test correct pour la frappe vers l'adresse nulle est `require(account != address(0), "")`, que le contrat ne prend jamais la peine de vérifier. + +```solidity + _totalSupply = _totalSupply.add(amount); + _balances[contract_owner] = _balances[contract_owner].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +Il y a deux autres faits suspects, directement liés à la frappe : + +- Il y a un paramètre `account`, qui est vraisemblablement le compte qui devrait recevoir le montant frappé. Mais le solde qui augmente est en fait celui de `contract_owner`. + +- Alors que le solde augmenté appartient à `contract_owner`, l'événement émis montre un transfert vers `account`. + +### Pourquoi `auth` et `approver` à la fois ? Pourquoi le `mod` qui ne fait rien ? {#why-both-autho-and-approver-why-the-mod-that-does-nothing} + +Ce contrat contient trois modificateurs : `_mod_`, `auth`, et `approver`. + +```solidity + modifier _mod_(address sender, address recipient, uint256 amount){ + _; + } +``` + +`_mod_` prend trois paramètres et n'en fait rien. Pourquoi l'avoir ? + +```solidity + modifier auth() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; + } + + modifier approver() { + require(msg.sender == contract_owner, "Not allowed to interact"); + _; + } +``` + +`auth` et `approver` sont plus logiques, car ils vérifient que le contrat a été appelé par `contract_owner`. Nous nous attendrions à ce que certaines actions privilégiées, comme la frappe, soient limitées à ce compte. Cependant, quel est l'intérêt d'avoir deux fonctions distinctes qui font _précisément la même chose_ ? + +## Que pouvons-nous détecter automatiquement ? {#what-can-we-detect-automatically} + +Nous pouvons voir que `wARB` est un jeton frauduleux en regardant sur Etherscan. Cependant, c'est une solution centralisée. En théorie, Etherscan pourrait être subverti ou piraté. Il est préférable de pouvoir déterminer indépendamment si un jeton est légitime ou non. + +Il y a quelques astuces que nous pouvons utiliser pour identifier qu'un jeton ERC-20 est suspect (soit une escroquerie, soit très mal écrit), en regardant les événements qu'il émet. + +## Événements `Approval` suspects {#suspicious-approval-events} + +Les [événements `Approval`](https://eips.ethereum.org/EIPS/eip-20#approval) ne devraient se produire qu'avec une demande directe (contrairement aux [événements `Transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer-1) qui peuvent se produire à la suite d'une allocation). [Consultez la documentation de Solidity](https://docs.soliditylang.org/en/v0.8.20/security-considerations.html#tx-origin) pour une explication détaillée de ce problème et pourquoi les requêtes doivent être directes, plutôt que médiatisées par un contrat. + +Cela signifie que les événements `Approval` qui approuvent les dépenses d'un [compte externe](/developers/docs/accounts/#types-of-account) doivent provenir de transactions qui proviennent de ce compte, et dont la destination est le contrat ERC-20. Toute autre type d'approbation d'un compte externe est suspect. + +Voici [un programme qui identifie ce type d'événement](https://github.com/qbzzt/20230915-scam-token-detection), utilisant [viem](https://viem.sh/) et [TypeScript](https://www.typescriptlang.org/docs/), une variante de JavaScript avec une sécurité de type. Pour l'exécuter : + +1. Copiez `.env.example` dans `.env`. +2. Modifiez `.env` pour fournir l'URL d'un nœud du réseau principal Ethereum. +3. Exécutez `pnpm install` pour installer les paquets nécessaires. +4. Exécutez `pnpm susApproval` pour rechercher les approbations suspectes. + +Voici une explication ligne par ligne : + +```typescript +import { + Address, + TransactionReceipt, + createPublicClient, + http, + parseAbiItem, +} from "viem" +import { mainnet } from "viem/chains" +``` + +Importez les définitions de type, les fonctions, et la définition de la chaîne depuis `viem`. + +```typescript +import { config } from "dotenv" +config() +``` + +Lisez `.env` pour obtenir l'URL. + +```typescript +const client = createPublicClient({ + chain: mainnet, + transport: http(process.env.URL), +}) +``` + +Créez un client Viem. Nous n'avons besoin que de lire à partir de la blockchain, donc ce client n'a pas besoin d'une clé privée. + +```typescript +const testedAddress = "0xb047c8032b99841713b8e3872f06cf32beb27b82" +const fromBlock = 16859812n +const toBlock = 16873372n +``` + +L'adresse du contrat ERC-20 suspect, et les blocs dans lesquels nous chercherons des événements. Les fournisseurs de nœuds limitent généralement notre capacité à lire les événements car la bande passante peut devenir coûteuse. Heureusement, `wARB` n'a pas été utilisé pendant une période de dix-huit heures, nous pouvons donc rechercher tous les événements (il n'y en avait que 13 au total). + +```typescript +const approvalEvents = await client.getLogs({ + address: testedAddress, + fromBlock, + toBlock, + event: parseAbiItem( + "event Approval(address indexed _owner, address indexed _spender, uint256 _value)" + ), +}) +``` + +C'est la façon de demander à Viem des informations sur les événements. Lorsque nous lui fournissons la signature exacte de l'événement, y compris les noms de champs, il analyse l'événement pour nous. + +```typescript +const isContract = async (addr: Address): boolean => + await client.getBytecode({ address: addr }) +``` + +Notre algorithme ne s'applique qu'aux comptes externes. Si un bytecode est retourné par `client.getBytecode`, cela signifie qu'il s'agit d'un contrat et que nous devrions simplement l'ignorer. + +Si vous n'avez jamais utilisé TypeScript auparavant, la définition de la fonction peut sembler un peu bizarre. Nous ne lui disons pas seulement que le premier (et unique) paramètre s'appelle `addr`, mais aussi qu'il est de type `Address`. De même, la partie `: boolean` indique à TypeScript que la valeur de retour de la fonction est un booléen. + +```typescript +const getEventTxn = async (ev: Event): TransactionReceipt => + await client.getTransactionReceipt({ hash: ev.transactionHash }) +``` + +Cette fonction obtient le reçu de transaction d'un événement. Nous avons besoin du reçu pour nous assurer que nous connaissons la destination de la transaction. + +```typescript +const suspiciousApprovalEvent = async (ev : Event) : (Event | null) => { +``` + +C'est la fonction la plus importante, celle qui décide réellement si un événement est suspect ou non. Le type de retour, `(Event | null)`, indique à TypeScript que cette fonction peut retourner soit un `Event`, soit `null`. Nous retournons `null` si l'événement n'est pas suspect. + +```typescript +const owner = ev.args._owner +``` + +Viem a les noms de champs, il a donc analysé l'événement pour nous. `_owner` est le propriétaire des jetons à dépenser. + +```typescript +// Les approbations par les contrats ne sont pas suspectes +if (await isContract(owner)) return null +``` + +Si le propriétaire est un contrat, supposez que cette approbation n'est pas suspecte. Pour vérifier si l'approbation d'un contrat est suspecte ou non, nous devrons tracer l'exécution complète de la transaction pour voir si elle a atteint le contrat propriétaire, et si ce contrat a appelé directement le contrat ERC-20. C'est beaucoup plus coûteux en ressources que ce que nous aimerions faire. + +```typescript +const txn = await getEventTxn(ev) +``` + +Si l'approbation provient d'un compte externe, obtenez la transaction qui l'a causée. + +```typescript +// L'approbation est suspecte si elle provient d'un propriétaire EOA qui n'est pas le `from` de la transaction +if (owner.toLowerCase() != txn.from.toLowerCase()) return ev +``` + +Nous ne pouvons pas simplement vérifier l'égalité des chaînes de caractères car les adresses sont hexadécimales, donc elles contiennent des lettres. Parfois, par exemple dans `txn.from`, ces lettres sont toutes en minuscules. Dans d'autres cas, comme `ev.args._owner`, l'adresse est en [casse mixte pour l'identification d'erreurs](https://eips.ethereum.org/EIPS/eip-55). + +Mais si la transaction ne provient pas du propriétaire, et que ce propriétaire est détenu par un externe, alors nous avons une transaction suspecte. + +```typescript +// C'est aussi suspect si la destination de la transaction n'est pas le contrat ERC-20 que nous +// examinons +if (txn.to.toLowerCase() != testedAddress) return ev +``` + +De même, si l'adresse `to` de la transaction, le premier contrat appelé, n'est pas le contrat ERC-20 sous investigation, alors c'est suspect. + +```typescript + // S'il n'y a aucune raison d'être suspect, retourner null. + return null +} +``` + +Si aucune des deux conditions n'est vraie, alors l'événement `Approval` n'est pas suspect. + +```typescript +const testPromises = approvalEvents.map((ev) => suspiciousApprovalEvent(ev)) +const testResults = (await Promise.all(testPromises)).filter((x) => x != null) + +console.log(testResults) +``` + +[Une fonction `async`](https://www.w3schools.com/js/js_async.asp) retourne un objet `Promise`. Avec la syntaxe courante, `await x()`, nous attendons que cette `Promise` soit remplie avant de continuer le traitement. C'est simple à programmer et à suivre, mais c'est aussi inefficace. Pendant que nous attendons que la `Promise` d'un événement spécifique soit remplie, nous pouvons déjà commencer à travailler sur l'événement suivant. + +Ici, nous utilisons [`map`](https://www.w3schools.com/jsref/jsref_map.asp) pour créer un tableau d'objets `Promise`. Ensuite, nous utilisons [`Promise.all`](https://www.javascripttutorial.net/es6/javascript-promise-all/) pour attendre que toutes ces promesses soient résolues. Nous [`filtrons`](https://www.w3schools.com/jsref/jsref_filter.asp) ensuite ces résultats pour supprimer les événements non suspects. + +### Événements `Transfer` suspects {#suspicious-transfer-events} + +Une autre façon possible d'identifier les jetons frauduleux est de voir s'ils ont des transferts suspects. Par exemple, les transferts provenant de comptes qui n'ont pas autant de jetons. Vous pouvez voir [comment implémenter ce test](https://github.com/qbzzt/20230915-scam-token-detection/blob/main/susTransfer.ts), mais `wARB` n'a pas ce problème. + +## Conclusion {#conclusion} + +La détection automatisée des escroqueries ERC-20 souffre de [faux négatifs](https://en.wikipedia.org/wiki/False_positives_and_false_negatives#False_negative_error), car une escroquerie peut utiliser un contrat de jeton ERC-20 parfaitement normal qui ne représente simplement rien de réel. Vous devriez donc toujours essayer d'_obtenir l'adresse du jeton d'une source de confiance_. + +La détection automatisée peut aider dans certains cas, comme pour les éléments de la DeFi, où il y a de nombreux jetons qui doivent être gérés automatiquement. Mais comme toujours [caveat emptor](https://www.investopedia.com/terms/c/caveatemptor.asp), faites vos propres recherches, et encouragez vos utilisateurs à faire de même. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/secret-state/index.md b/public/content/translations/fr/developers/tutorials/secret-state/index.md new file mode 100644 index 00000000000..ac12823930c --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/secret-state/index.md @@ -0,0 +1,741 @@ +--- +title: "Utiliser la preuve à divulgation nulle de connaissance pour un état secret" +description: "les jeux en chaîne sont limités car ils ne peuvent pas conserver d'informations cachées. Après avoir lu ce tutoriel, un lecteur sera en mesure de combiner des preuves à divulgation nulle de connaissance et des composants de serveur pour créer des jeux vérifiables avec un état secret, hors chaîne, composant. La technique pour ce faire sera démontrée en créant un jeu de démineur." +author: Ori Pomerantz +tags: + [ + "serveur", + "hors-chaîne", + "centralisé", + "preuve à divulgation nulle de connaissance", + "zokrates", + "mud" + ] +skill: advanced +lang: fr +published: 2025-03-15 +--- + +_Il n'y a pas de secret sur la blockchain_. Tout ce qui est publié sur la blockchain est ouvert à la lecture de tous. C'est nécessaire, car la blockchain est basée sur le fait que tout le monde peut la vérifier. Cependant, les jeux reposent souvent sur un état secret. Par exemple, le jeu du [démineur](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\)) n'a absolument aucun sens si vous pouvez simplement aller sur un explorateur de blockchain et voir la carte. + +La solution la plus simple est d'utiliser un [composant serveur](/developers/tutorials/server-components/) pour contenir l'état secret. Cependant, la raison pour laquelle nous utilisons la blockchain est d'empêcher la triche par le développeur du jeu. Nous devons garantir l'honnêteté du composant serveur. Le serveur peut fournir un hachage de l'état, et utiliser des [preuves à divulgation nulle de connaissance](/zero-knowledge-proofs/#why-zero-knowledge-proofs-are-important) pour prouver que l'état utilisé pour calculer le résultat d'un mouvement est le bon. + +Après avoir lu cet article, vous saurez comment créer ce type de serveur de maintien d'état secret, un client pour montrer l'état, et un composant en chaîne pour la communication entre les deux. Les principaux outils que nous utiliserons seront : + +| Outil | Objectif | Vérifié sur la version | +| --------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------: | +| [Zokrates](https://zokrates.github.io/) | Preuves à divulgation nulle de connaissance et leur vérification | 1.1.9 | +| [Typescript](https://www.typescriptlang.org/) | Langage de programmation pour le serveur et le client | 5.4.2 | +| [Node](https://nodejs.org/en) | Exécution du serveur | 20.18.2 | +| [Viem](https://viem.sh/) | Communication avec la Blockchain | 2.9.20 | +| [MUD](https://mud.dev/) | Gestion des données en chaîne | 2.0.12 | +| [React](https://react.dev/) | Interface utilisateur du client | 18.2.0 | +| [Vite](https://vitejs.dev/) | Servir le code client | 4.2.1 | + +## Exemple de démineur {#minesweeper} + +Le [démineur](https://en.wikipedia.org/wiki/Minesweeper_\(video_game\)) est un jeu qui comprend une carte secrète avec un champ de mines. Le joueur choisit de creuser à un endroit précis. Si cet emplacement contient une mine, la partie est terminée. Sinon, le joueur obtient le nombre de mines dans les huit cases environnantes. + +Cette application est écrite en utilisant [MUD](https://mud.dev/), un framework qui nous permet de stocker des données en chaîne en utilisant une [base de données clé-valeur](https://aws.amazon.com/nosql/key-value/) et de synchroniser ces données automatiquement avec des composants hors chaîne. En plus de la synchronisation, MUD facilite le contrôle d'accès et permet aux autres utilisateurs d'[étendre](https://mud.dev/guides/extending-a-world) notre application sans permission. + +### Exécuter l'exemple du démineur {#running-minesweeper-example} + +Pour exécuter l'exemple du démineur : + +1. Assurez-vous d'[avoir installé les prérequis](https://mud.dev/quickstart#prerequisites) : [Node](https://mud.dev/quickstart#prerequisites), [Foundry](https://book.getfoundry.sh/getting-started/installation), [`git`](https://git-scm.com/downloads), [`pnpm`](https://git-scm.com/downloads), et [`mprocs`](https://github.com/pvolok/mprocs). + +2. Cloner le dépôt. + + ```sh copy + git clone https://github.com/qbzzt/20240901-secret-state.git + ``` + +3. Installez les paquets. + + ```sh copy + cd 20240901-secret-state/ + pnpm install + npm install -g mprocs + ``` + + Si Foundry a été installé dans le cadre de `pnpm install`, vous devez redémarrer le shell de ligne de commande. + +4. Compiler les contrats + + ```sh copy + cd packages/contracts + forge build + cd ../.. + ``` + +5. Démarrez le programme (y compris une blockchain [anvil](https://book.getfoundry.sh/anvil/)) et attendez. + + ```sh copy + mprocs + ``` + + Notez que le démarrage prend beaucoup de temps. Pour voir la progression, utilisez d'abord la flèche vers le bas pour faire défiler jusqu'à l'onglet _contracts_ pour voir les contrats MUD en cours de déploiement. Lorsque vous recevez le message _Waiting for file changes…_, les contrats sont déployés et la suite de la progression se déroulera dans l'onglet _server_. Là, vous attendez de recevoir le message _Verifier address: 0x...._. + + Si cette étape est réussie, vous verrez l'écran `mprocs`, avec les différents processus à gauche et la sortie de la console pour le processus actuellement sélectionné à droite. + + ![L'écran mprocs](./mprocs.png) + + En cas de problème avec `mprocs`, vous pouvez exécuter les quatre processus manuellement, chacun dans sa propre fenêtre de ligne de commande : + + - **Anvil** + + ```sh + cd packages/contracts + anvil --base-fee 0 --block-time 2 + ``` + + - **Contrats** + + ```sh + cd packages/contracts + pnpm mud dev-contracts --rpc http://127.0.0.1:8545 + ``` + + - **Serveur** + + ```sh + cd packages/server + pnpm start + ``` + + - **Client** + + ```sh + cd packages/client + pnpm run dev + ``` + +6. Vous pouvez maintenant naviguer vers [le client](http://localhost:3000), cliquer sur **New Game** et commencer à jouer. + +### Tables {#tables} + +Nous avons besoin de [plusieurs tables](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts) en chaîne. + +- `Configuration` : cette table est un singleton, elle n'a pas de clé et un seul enregistrement. Elle est utilisée pour contenir les informations de configuration du jeu : + - `height` : la hauteur d'un champ de mines + - `width` : la largeur d'un champ de mines + - `numberOfBombs` : le nombre de bombes dans chaque champ de mines + +- `VerifierAddress`: cette table est également un singleton. Elle est utilisée pour contenir une partie de la configuration, l'adresse du contrat de vérification (`verifier`). Nous aurions pu mettre cette information dans la table `Configuration`, mais elle est définie par un composant différent, le serveur, il est donc plus facile de la mettre dans une table séparée. + +- `PlayerGame` : la clé est l'adresse du joueur. Les données sont : + + - `gameId` : valeur de 32 octets qui est le hachage de la carte sur laquelle le joueur joue (l'identifiant du jeu). + - `win` : un booléen indiquant si le joueur a gagné la partie. + - `lose` : un booléen indiquant si le joueur a perdu la partie. + - `digNumber` : le nombre de creusements réussis dans le jeu. + +- `GamePlayer` : cette table contient le mappage inverse, de `gameId` à l'adresse du joueur. + +- `Map` : la clé est un tuple de trois valeurs : + + - `gameId` : valeur de 32 octets qui est le hachage de la carte sur laquelle le joueur joue (l'identifiant du jeu). + - Coordonnée `x` + - Coordonnée `y` + + La valeur est un nombre unique. C'est 255 si une bombe a été détectée. Sinon, c'est le nombre de bombes autour de cet emplacement plus un. Nous ne pouvons pas simplement utiliser le nombre de bombes, car par défaut, tout le stockage dans l'EVM et toutes les valeurs de lignes dans MUD sont à zéro. Nous devons faire la distinction entre « le joueur n'a pas encore creusé ici » et « le joueur a creusé ici, et a trouvé qu'il n'y avait aucune bombe aux alentours ». + +De plus, la communication entre le client et le serveur se fait via le composant en chaîne. Ceci est également implémenté en utilisant des tables. + +- `PendingGame`: demandes non traitées pour démarrer une nouvelle partie. +- `PendingDig`: demandes non traitées pour creuser à un endroit spécifique dans un jeu spécifique. Il s'agit d'une [table hors chaîne](https://mud.dev/store/tables#types-of-tables), ce qui signifie qu'elle n'est pas écrite dans le stockage EVM, elle n'est lisible qu'en dehors de la chaîne en utilisant des événements. + +### Flux d'exécution et de données {#execution-data-flows} + +Ces flux coordonnent l'exécution entre le client, le composant en chaîne et le serveur. + +#### Initialisation {#initialization-flow} + +Lorsque vous exécutez `mprocs`, ces étapes se produisent : + +1. [`mprocs`](https://github.com/pvolok/mprocs) exécute quatre composants : + + - [Anvil](https://book.getfoundry.sh/anvil/), qui exécute une blockchain locale + - [Contracts](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/contracts), qui compile (si nécessaire) et déploie les contrats pour MUD + - [Client](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/client), qui exécute [Vite](https://vitejs.dev/) pour servir l'interface utilisateur et le code client aux navigateurs Web. + - [Serveur](https://github.com/qbzzt/20240901-secret-state/tree/main/packages/server), qui effectue les actions du serveur + +2. Le paquet `contracts` déploie les contrats MUD puis exécute [le script `PostDeploy.s.sol`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol). Ce script définit la configuration. Le code de github spécifie [un champ de mines de 10x5 avec huit mines](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/script/PostDeploy.s.sol#L23). + +3. [Le serveur](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts) commence par [configurer MUD](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L6). Entre autres, cela active la synchronisation des données, de sorte qu'une copie des tables pertinentes existe dans la mémoire du serveur. + +4. Le serveur abonne une fonction à exécuter [lorsque la table `Configuration` change](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L23). [Cette fonction](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L24-L168) est appelée après l'exécution de `PostDeploy.s.sol` et la modification de la table. + +5. Lorsque la fonction d'initialisation du serveur dispose de la configuration, [elle appelle `zkFunctions`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L34-L35) pour initialiser [la partie preuve à divulgation nulle de connaissance du serveur](#using-zokrates-from-typescript). Cela ne peut pas se produire tant que nous n'avons pas la configuration, car les fonctions de preuve à divulgation nulle de connaissance doivent avoir la largeur et la hauteur du champ de mines comme constantes. + +6. Une fois que la partie preuve à divulgation nulle de connaissance du serveur est initialisée, l'étape suivante consiste à [déployer le contrat de vérification de la preuve à divulgation nulle de connaissance sur la blockchain](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L42-L53) et à définir l'adresse du vérifié dans MUD. + +7. Enfin, nous nous abonnons aux mises à jour pour savoir quand un joueur demande soit [de commencer une nouvelle partie](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71) soit de [creuser dans une partie existante](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73-L108). + +#### Nouvelle partie {#new-game-flow} + +Voici ce qui se passe lorsque le joueur demande une nouvelle partie. + +1. S'il n'y a pas de partie en cours pour ce joueur, ou s'il y en a une mais avec un gameId à zéro, le client affiche un [bouton nouvelle partie](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175). Lorsque l'utilisateur appuie sur ce bouton, [React exécute la fonction `newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L96). + +2. [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L43-L46) est un appel `System`. Dans MUD, tous les appels sont acheminés via le contrat `World`, et dans la plupart des cas, vous appelez `__`. Dans ce cas, l'appel est à `app__newGame`, que MUD achemine ensuite vers [`newGame` dans `GameSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L16-L22). + +3. La fonction en chaîne vérifie que le joueur n'a pas de partie en cours, et s'il n'y en a pas, [ajoute la demande à la table `PendingGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L21). + +4. Le serveur détecte le changement dans `PendingGame` et [exécute la fonction abonnée](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L55-L71). Cette fonction appelle [`newGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L110-L114), qui à son tour appelle [`createGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L116-L144). + +5. La première chose que `createGame` fait est de [créer une carte aléatoire avec le nombre de mines approprié](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L120-L135). Ensuite, elle appelle [`makeMapBorders`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L147-L166) pour créer une carte avec des bordures vides, ce qui est nécessaire pour Zokrates. Enfin, `createGame` appelle [`calculateMapHash`](#calculateMapHash), pour obtenir le hachage de la carte, qui est utilisé comme ID de jeu. + +6. La fonction `newGame` ajoute la nouvelle partie à `gamesInProgress`. + +7. La dernière chose que fait le serveur est d'appeler [`app__newGameResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L38-L43), qui est en chaîne. Cette fonction se trouve dans un `System` différent, [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol), pour permettre le contrôle d'accès. Le contrôle d'accès est défini dans le [fichier de configuration MUD](https://mud.dev/config), [`mud.config.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/mud.config.ts#L67-L72). + + La liste d'accès n'autorise qu'une seule adresse à appeler le `System`. Cela restreint l'accès aux fonctions du serveur à une seule adresse, afin que personne ne puisse se faire passer pour le serveur. + +8. Le composant en chaîne met à jour les tables pertinentes : + + - Créer la partie dans `PlayerGame`. + - Définir le mappage inverse dans `GamePlayer`. + - Supprimer la demande de `PendingGame`. + +9. Le serveur identifie le changement dans `PendingGame`, mais ne fait rien car [`wantsGame`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L58-L60) est faux. + +10. Sur le client, [`gameRecord`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L143-L148) est défini sur l'entrée `PlayerGame` pour l'adresse du joueur. Lorsque `PlayerGame` change, `gameRecord` change aussi. + +11. S'il y a une valeur dans `gameRecord`, et que la partie n'a été ni gagnée ni perdue, le client [affiche la carte](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190). + +#### Creuser {#dig-flow} + +1. Le joueur [clique sur le bouton de la cellule de la carte](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L188), ce qui appelle [la fonction `dig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/mud/createSystemCalls.ts#L33-L36). Cette fonction appelle [`dig` en chaîne](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L24-L32). + +2. Le composant en chaîne [effectue un certain nombre de vérifications de cohérence](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L25-L30), et en cas de succès, ajoute la demande de creusage à [`PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/GameSystem.sol#L31). + +3. Le serveur [détecte le changement dans `PendingDig`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L73). [Si elle est valide](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L75-L84), il [appelle le code de preuve à divulgation nulle de connaissance](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L86-L95) (expliqué ci-dessous) pour générer à la fois le résultat et une preuve de sa validité. + +4. [Le serveur](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L97-L107) appelle [`digResponse`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L45-L64) en chaîne. + +5. `digResponse` fait deux choses. Tout d'abord, il vérifie [la preuve à divulgation nulle de connaissance](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L47-L61). Ensuite, si la preuve est valide, il appelle [`processDigResult`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L67-L86) pour traiter réellement le résultat. + +6. `processDigResult` vérifie si la partie a été [perdue](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L76-L78) ou [gagnée](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L83-L86), et [met à jour `Map`, la carte en chaîne](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol#L80). + +7. Le client récupère automatiquement les mises à jour et [met à jour la carte affichée au joueur](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/client/src/App.tsx#L175-L190), et le cas échéant, informe le joueur s'il s'agit d'une victoire ou d'une défaite. + +## Utiliser Zokrates {#using-zokrates} + +Dans les flux expliqués ci-dessus, nous avons ignoré les parties concernant la preuve à divulgation nulle de connaissance, en les traitant comme une boîte noire. Maintenant, ouvrons-la et voyons comment ce code est écrit. + +### Hachage de la carte {#hashing-map} + +Nous pouvons utiliser [ce code JavaScript](https://github.com/ZK-Plus/ICBC24_Tutorial_Compute-Offchain-Verify-onchain/tree/solutions/exercise) pour implémenter [Poseidon](https://www.poseidon-hash.info), la fonction de hachage Zokrates que nous utilisons. Cependant, bien que cela serait plus rapide, ce serait aussi plus compliqué que de simplement utiliser la fonction de hachage Zokrates pour le faire. Ceci est un tutoriel, et le code est donc optimisé pour la simplicité, non pour la performance. Par conséquent, nous avons besoin de deux programmes Zokrates différents, un pour calculer simplement le hachage d'une carte (`hash`) et un pour créer réellement une preuve à divulgation nulle de connaissance du résultat du creusement à un emplacement sur la carte (`dig`). + +### La fonction de hachage {#hash-function} + +C'est la fonction qui calcule le hachage d'une carte. Nous allons parcourir ce code ligne par ligne. + +``` +import "hashes/poseidon/poseidon.zok" as poseidon; +import "utils/pack/bool/pack128.zok" as pack128; +``` + +Ces deux lignes importent deux fonctions de la [bibliothèque standard de Zokrates](https://zokrates.github.io/toolbox/stdlib.html). [La première fonction](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/hashes/poseidon/poseidon.zok) est un [hachage Poseidon](https://www.poseidon-hash.info/). Elle prend un tableau d'éléments [`field`](https://zokrates.github.io/language/types.html#field) et renvoie un `field`. + +L'élément field dans Zokrates est généralement inférieur à 256 bits, mais pas de beaucoup. Pour simplifier le code, nous limitons la carte à 512 bits et nous hachons un tableau de quatre champs, et dans chaque champ, nous n'utilisons que 128 bits. La fonction [`pack128`](https://github.com/Zokrates/ZoKrates/blob/latest/zokrates_stdlib/stdlib/utils/pack/bool/pack128.zok) transforme un tableau de 128 bits en un `field` à cet effet. + +``` + def hashMap(bool[${width+2}][${height+2}] map) -> field { +``` + +Cette ligne commence une définition de fonction. `hashMap` reçoit un seul paramètre appelé `map`, un tableau `bool`(éen) à deux dimensions. La taille de la carte est de `width+2` par `height+2` pour des raisons qui sont [expliquées ci-dessous](#why-map-border). + +Nous pouvons utiliser `${width+2}` et `${height+2}` car les programmes Zokrates sont stockés dans cette application sous forme de [modèles de chaînes de caractères](https://www.w3schools.com/js/js_string_templates.asp). Le code entre `${` et `}` est évalué par JavaScript, et de cette manière, le programme peut être utilisé pour différentes tailles de carte. Le paramètre map a une bordure d'un emplacement de large tout autour sans aucune bombe, c'est la raison pour laquelle nous devons ajouter deux à la largeur et à la hauteur. + +La valeur de retour est un `field` qui contient le hachage. + +``` + bool[512] mut map1d = [false; 512]; +``` + +La carte est bidimensionnelle. Cependant, la fonction `pack128` ne fonctionne pas avec des tableaux bidimensionnels. Nous aplatissons donc d'abord la carte en un tableau de 512 octets, en utilisant `map1d`. Par défaut, les variables Zokrates sont des constantes, mais nous devons assigner des valeurs à ce tableau dans une boucle, nous le définissons donc comme [`mut`](https://zokrates.github.io/language/variables.html#mutability). + +Nous devons initialiser le tableau car Zokrates n'a pas de `undefined`. L'expression `[false; 512]` signifie [un tableau de 512 valeurs `false`](https://zokrates.github.io/language/types.html#declaration-and-initialization). + +``` + u32 mut counter = 0; +``` + +Nous avons également besoin d'un compteur pour distinguer les bits que nous avons déjà remplis dans `map1d` de ceux que nous n'avons pas encore remplis. + +``` + for u32 x in 0..${width+2} { +``` + +Voici comment déclarer une boucle [`for`](https://zokrates.github.io/language/control_flow.html#for-loops) en Zokrates. Une boucle `for` de Zokrates doit avoir des limites fixes, car bien qu'elle semble être une boucle, le compilateur la « déroule » en réalité. L'expression `${width+2}` est une constante de temps de compilation car `width` est définie par le code TypeScript avant d'appeler le compilateur. + +``` + for u32 y in 0..${height+2} { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } +``` + +Pour chaque emplacement de la carte, mettez cette valeur dans le tableau `map1d` et incrémentez le compteur. + +``` + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; +``` + +Le `pack128` pour créer un tableau de quatre valeurs `field` à partir de `map1d`. En Zokrates, `array[a..b]` signifie la tranche du tableau qui commence à `a` et se termine à `b-1`. + +``` + return poseidon(hashMe); +} +``` + +Utilisez `poseidon` pour convertir ce tableau en un hachage. + +### Le programme de hachage {#hash-program} + +Le serveur doit appeler `hashMap` directement pour créer des identifiants de jeu. Cependant, Zokrates ne peut appeler que la fonction `main` d'un programme pour démarrer, nous créons donc un programme avec une fonction `main` qui appelle la fonction de hachage. + +``` +${hashFragment} + +def main(bool[${width+2}][${height+2}] map) -> field { + return hashMap(map); +} +``` + +### Le programme de creusage {#dig-program} + +C'est le cœur de la partie preuve à divulgation nulle de connaissance de l'application, où nous produisons les preuves qui sont utilisées pour vérifier les résultats des creusages. + +``` +${hashFragment} + +// Le nombre de mines à l'emplacement (x,y) +def map2mineCount(bool[${width+2}][${height+2}] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; +} +``` + +#### Pourquoi une bordure de carte {#why-map-border} + +Les preuves à divulgation nulle de connaissance utilisent des [circuits arithmétiques](https://medium.com/web3studio/simple-explanations-of-arithmetic-circuits-and-zero-knowledge-proofs-806e59a79785), qui n'ont pas d'équivalent facile à une instruction `if`. Au lieu de cela, ils utilisent l'équivalent de l'[opérateur conditionnel](https://en.wikipedia.org/wiki/Ternary_conditional_operator). Si `a` peut être soit zéro soit un, vous pouvez calculer `if a { b } else { c }` comme `ab+(1-a)c`. + +Pour cette raison, une instruction `if` de Zokrates évalue toujours les deux branches. Par exemple, si vous avez ce code : + +``` +bool[5] arr = [false; 5]; +u32 index=10; +return if index>4 { 0 } else { arr[index] } +``` + +Il générera une erreur, car il a besoin de calculer `arr[10]`, même si cette valeur sera plus tard multipliée par zéro. + +C'est la raison pour laquelle nous avons besoin d'une bordure d'un emplacement de large tout autour de la carte. Nous devons calculer le nombre total de mines autour d'un emplacement, ce qui signifie que nous devons voir l'emplacement une ligne au-dessus et en dessous, à gauche et à droite de l'emplacement où nous creusons. Ce qui signifie que ces emplacements doivent exister dans le tableau de la carte qui est fourni à Zokrates. + +``` +def main(private bool[${width+2}][${height+2}] map, u32 x, u32 y) -> (field, u8) { +``` + +Par défaut, les preuves Zokrates incluent leurs entrées. Il est inutile de savoir qu'il y a cinq mines autour d'un endroit si vous ne savez pas réellement de quel endroit il s'agit (et vous ne pouvez pas simplement le faire correspondre à votre demande, car le prouveur pourrait alors utiliser des valeurs différentes sans vous en informer). Cependant, nous devons garder la carte secrète, tout en la fournissant à Zokrates. La solution est d'utiliser un paramètre `private`, un paramètre qui n'est _pas_ révélé par la preuve. + +Cela ouvre une autre voie d'abus. Le prouveur pourrait utiliser les bonnes coordonnées, mais créer une carte avec un nombre quelconque de mines autour de l'emplacement, et éventuellement à l'emplacement même. Pour empêcher cet abus, nous faisons en sorte que la preuve à divulgation nulle de connaissance inclue le hachage de la carte, qui est l'identifiant de la partie. + +``` + return (hashMap(map), +``` + +La valeur de retour ici est un tuple qui inclut le tableau de hachage de la carte ainsi que le résultat du creusage. + +``` + if map2mineCount(map, x, y) > 0 { 0xFF } else { +``` + +Nous utilisons 255 comme valeur spéciale au cas où l'emplacement lui-même contiendrait une bombe. + +``` + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); +} +``` + +Si le joueur n'a pas touché de mine, ajoutez les nombres de mines pour la zone autour de l'emplacement et retournez ce résultat. + +### Utiliser Zokrates depuis TypeScript {#using-zokrates-from-typescript} + +Zokrates a une interface en ligne de commande, mais dans ce programme, nous l'utilisons dans le [code TypeScript](https://zokrates.github.io/toolbox/zokrates_js.html). + +La bibliothèque qui contient les définitions de Zokrates s'appelle [`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts). + +```typescript +import { initialize as zokratesInitialize } from "zokrates-js" +``` + +Importer les [liaisons JavaScript de Zokrates](https://zokrates.github.io/toolbox/zokrates_js.html). Nous n'avons besoin que de la fonction [`initialize`](https://zokrates.github.io/toolbox/zokrates_js.html#initialize) car elle renvoie une promesse qui se résout en toutes les définitions de Zokrates. + +```typescript +export const zkFunctions = async (width: number, height: number) : Promise => { +``` + +Similaire à Zokrates lui-même, nous n'exportons qu'une seule fonction, qui est également [asynchrone](https://www.w3schools.com/js/js_async.asp). Lorsqu'elle finit par retourner un résultat, elle fournit plusieurs fonctions comme nous le verrons ci-dessous. + +```typescript +const zokrates = await zokratesInitialize() +``` + +Initialiser Zokrates, obtenir tout ce dont nous avons besoin de la bibliothèque. + +```typescript +const hashFragment = ` + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + . + . + . + } + ` + +const hashProgram = ` + ${hashFragment} + . + . + . + ` + +const digProgram = ` + ${hashFragment} + . + . + . + ` +``` + +Ensuite, nous avons la fonction de hachage et les deux programmes Zokrates que nous avons vus ci-dessus. + +```typescript +const digCompiled = zokrates.compile(digProgram) +const hashCompiled = zokrates.compile(hashProgram) +``` + +Ici, nous compilons ces programmes. + +```typescript +// Créez les clés pour la vérification à divulgation nulle de connaissance. +// Sur un système de production, vous voudriez utiliser une cérémonie de configuration. +// (https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony). +const keySetupResults = zokrates.setup(digCompiled.program, "") +const verifierKey = keySetupResults.vk +const proverKey = keySetupResults.pk +``` + +Sur un système de production, nous pourrions utiliser une [cérémonie de configuration](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony) plus compliquée, mais cela suffit pour une démonstration. Ce n'est pas un problème que les utilisateurs puissent connaître la clé du prouveur - ils ne peuvent toujours pas l'utiliser pour prouver des choses à moins qu'elles ne soient vraies. Parce que nous spécifions l'entropie (le deuxième paramètre, `""`), les résultats seront toujours les mêmes. + +**Note :** La compilation des programmes Zokrates et la création des clés sont des processus lents. Il n'est pas nécessaire de les répéter à chaque fois, seulement lorsque la taille de la carte change. Sur un système de production, vous le feriez une fois, puis stockeriez le résultat. La seule raison pour laquelle je ne le fais pas ici est par souci de simplicité. + +#### `calculateMapHash` {#calculateMapHash} + +```typescript +const calculateMapHash = function (hashMe: boolean[][]): string { + return ( + "0x" + + BigInt(zokrates.computeWitness(hashCompiled, [hashMe]).output.slice(1, -1)) + .toString(16) + .padStart(64, "0") + ) +} +``` + +La fonction [`computeWitness`](https://zokrates.github.io/toolbox/zokrates_js.html#computewitnessartifacts-args-options) exécute réellement le programme Zokrates. Elle retourne une structure avec deux champs : `output`, qui est la sortie du programme sous forme de chaîne JSON, et `witness`, qui est l'information nécessaire pour créer une preuve à divulgation nulle de connaissance du résultat. Ici, nous avons juste besoin de la sortie. + +La sortie est une chaîne de caractères de la forme `"31337"`, un nombre décimal entre guillemets. Mais la sortie dont nous avons besoin pour `viem` est un nombre hexadécimal de la forme `0x60A7`. Donc, nous utilisons `.slice(1,-1)` pour supprimer les guillemets, puis `BigInt` pour transformer la chaîne restante, qui est un nombre décimal, en un [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). `.toString(16)` convertit ce `BigInt` en une chaîne hexadécimale, et `"0x"+` ajoute le marqueur pour les nombres hexadécimaux. + +```typescript +// Creuser et retourner une preuve à divulgation nulle de connaissance du résultat +// (code côté serveur) +``` + +La preuve à divulgation nulle de connaissance inclut les entrées publiques (`x` et `y`) et les résultats (hachage de la carte et nombre de bombes). + +```typescript + const zkDig = function(map: boolean[][], x: number, y: number) : any { + if (x<0 || x>=width || y<0 || y>=height) + throw new Error("Trying to dig outside the map") +``` + +C'est un problème de vérifier si un index est hors limites dans Zokrates, alors nous le faisons ici. + +```typescript +const runResults = zokrates.computeWitness(digCompiled, [map, `${x}`, `${y}`]) +``` + +Exécutez le programme de creusage. + +```typescript + const proof = zokrates.generateProof( + digCompiled.program, + runResults.witness, + proverKey) + + return proof + } +``` + +Utilisez [`generateProof`](https://zokrates.github.io/toolbox/zokrates_js.html#generateproofprogram-witness-provingkey-entropy) et retournez la preuve. + +```typescript +const solidityVerifier = ` + // Map size: ${width} x ${height} + \n${zokrates.exportSolidityVerifier(verifierKey)} + ` +``` + +Un vérificateur Solidity, un contrat intelligent que nous pouvons déployer sur la blockchain et utiliser pour vérifier les preuves générées par `digCompiled.program`. + +```typescript + return { + zkDig, + calculateMapHash, + solidityVerifier, + } +} +``` + +Enfin, retournez tout ce dont d'autres codes pourraient avoir besoin. + +## Tests de sécurité {#security-tests} + +Les tests de sécurité sont importants car un bug de fonctionnalité finira par se révéler. Mais si l'application n'est pas sécurisée, cela risque de rester caché pendant longtemps avant d'être révélé par quelqu'un qui triche et s'empare de ressources appartenant à d'autres. + +### Permissions {#permissions} + +Il y a une entité privilégiée dans ce jeu, le serveur. C'est le seul utilisateur autorisé à appeler les fonctions dans [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol). Nous pouvons utiliser [`cast`](https://book.getfoundry.sh/cast/) pour vérifier que les appels aux fonctions à permission sont autorisés uniquement en tant que compte serveur. + +[La clé privée du serveur se trouve dans `setupNetwork.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/mud/setupNetwork.ts#L52). + +1. Sur l'ordinateur qui exécute `anvil` (la blockchain), définissez ces variables d'environnement. + + ```sh copy + WORLD_ADDRESS=0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b + UNAUTHORIZED_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a + AUTHORIZED_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d + ``` + +2. Utilisez `cast` pour tenter de définir l'adresse du vérificateur en tant qu'adresse non autorisée. + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $UNAUTHORIZED_KEY + ``` + + Non seulement `cast` signale un échec, mais vous pouvez ouvrir les **Outils de développement MUD** dans le jeu sur le navigateur, cliquer sur **Tables** et sélectionner **app\_\_VerifierAddress**. Vous voyez que l'adresse n'est pas zéro. + +3. Définissez l'adresse du vérificateur comme l'adresse du serveur. + + ```sh copy + cast send $WORLD_ADDRESS 'app__setVerifier(address)' `cast address-zero` --private-key $AUTHORIZED_KEY + ``` + + L'adresse dans **app\_\_VerifiedAddress** devrait maintenant être à zéro. + +Toutes les fonctions MUD dans le même `System` passent par le même contrôle d'accès, donc je considère ce test suffisant. Si vous n'êtes pas d'accord, vous pouvez vérifier les autres fonctions dans [`ServerSystem`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/contracts/src/systems/ServerSystem.sol). + +### Abus de la preuve à divulgation nulle de connaissance {#zero-knowledge-abuses} + +La mathématique pour vérifier Zokrates dépasse le cadre de ce tutoriel (et mes capacités). Cependant, nous pouvons exécuter diverses vérifications sur le code de preuve à divulgation nulle de connaissance pour vérifier que s'il n'est pas fait correctement, il échoue. Tous ces tests vont nous obliger à modifier [`zero-knowledge.ts`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts) et à redémarrer toute l'application. Il ne suffit pas de redémarrer le processus du serveur, car cela met l'application dans un état impossible (le joueur a une partie en cours, mais la partie n'est plus disponible pour le serveur). + +#### Mauvaise réponse {#wrong-answer} + +La possibilité la plus simple est de fournir la mauvaise réponse dans la preuve à divulgation nulle de connaissance. Pour ce faire, nous allons dans `zkDig` et [modifions la ligne 91](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L91) : + +```ts +proof.inputs[3] = "0x" + "1".padStart(64, "0") +``` + +Cela signifie que nous prétendrons toujours qu'il y a une bombe, quelle que soit la bonne réponse. Essayez de jouer avec cette version, et vous verrez dans l'onglet **server** de l'écran `pnpm dev` cette erreur : + +``` + cause: { + code: 3, + message: 'execution reverted: revert: Zero knowledge verification fail', + data: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000 +00000000000000000000000000000000000000000000000205a65726f206b6e6f776c6564676520766572696669636174696f6 +e206661696c' + }, +``` + +Donc, ce genre de triche échoue. + +#### Mauvaise preuve {#wrong-proof} + +Que se passe-t-il si nous fournissons les bonnes informations, mais que les données de la preuve sont incorrectes ? Maintenant, remplacez la ligne 91 par : + +```ts +proof.proof = { + a: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + b: [ + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], + ], + c: ["0x" + "1".padStart(64, "0"), "0x" + "2".padStart(64, "0")], +} +``` + +Cela échoue toujours, mais maintenant cela échoue sans raison car cela se produit pendant l'appel du vérificateur. + +### Comment un utilisateur peut-il vérifier le code de confiance zéro ? {#user-verify-zero-trust} + +Les contrats intelligents sont relativement faciles à vérifier. Généralement, le développeur publie le code source sur un explorateur de blocs, et l'explorateur de blocs vérifie que le code source se compile bien en le code dans la [transaction de déploiement du contrat](/developers/docs/smart-contracts/deploying/). Dans le cas des `System` MUD, c'est [légèrement plus compliqué](https://mud.dev/cli/verify), mais pas de beaucoup. + +C'est plus difficile avec la preuve à divulgation nulle de connaissance. Le vérificateur inclut quelques constantes et exécute des calculs sur celles-ci. Cela ne vous dit pas ce qui est prouvé. + +```solidity + function verifyingKey() pure internal returns (VerifyingKey memory vk) { + vk.alpha = Pairing.G1Point(uint256(0x0f43f4fe7b5c2326fed4ac6ed2f4003ab9ab4ea6f667c2bdd77afb068617ee16), uint256(0x25a77832283f9726935219b5f4678842cda465631e72dbb24708a97ba5d0ce6f)); + vk.beta = Pairing.G2Point([uint256(0x2cebd0fbd21aca01910581537b21ae4fed46bc0e524c055059aa164ba0a6b62b), uint256(0x18fd4a7bc386cf03a95af7163d5359165acc4e7961cb46519e6d9ee4a1e2b7e9)], [uint256(0x11449dee0199ef6d8eebfe43b548e875c69e7ce37705ee9a00c81fe52f11a009), uint256(0x066d0c83b32800d3f335bb9e8ed5e2924cf00e77e6ec28178592eac9898e1a00)]); +``` + +La solution, du moins jusqu'à ce que les explorateurs de blocs ajoutent la vérification Zokrates à leurs interfaces utilisateur, est que les développeurs d'applications mettent à disposition les programmes Zokrates, et qu'au moins certains utilisateurs les compilent eux-mêmes avec la clé de vérification appropriée. + +Pour ce faire : + +1. [Installez Zokrates](https://zokrates.github.io/gettingstarted.html). + +2. Créez un fichier, `dig.zok`, avec le programme Zokrates. Le code ci-dessous suppose que vous avez conservé la taille de carte originale, 10x5. + + ```zokrates + import "utils/pack/bool/pack128.zok" as pack128; + import "hashes/poseidon/poseidon.zok" as poseidon; + + def hashMap(bool[12][7] map) -> field { + bool[512] mut map1d = [false; 512]; + u32 mut counter = 0; + + for u32 x in 0..12 { + for u32 y in 0..7 { + map1d[counter] = map[x][y]; + counter = counter+1; + } + } + + field[4] hashMe = [ + pack128(map1d[0..128]), + pack128(map1d[128..256]), + pack128(map1d[256..384]), + pack128(map1d[384..512]) + ]; + + return poseidon(hashMe); + } + + + // Le nombre de mines à l'emplacement (x,y) + def map2mineCount(bool[12][7] map, u32 x, u32 y) -> u8 { + return if map[x+1][y+1] { 1 } else { 0 }; + } + + def main(private bool[12][7] map, u32 x, u32 y) -> (field, u8) { + return (hashMap(map) , + if map2mineCount(map, x, y) > 0 { 0xFF } else { + map2mineCount(map, x-1, y-1) + map2mineCount(map, x, y-1) + map2mineCount(map, x+1, y-1) + + map2mineCount(map, x-1, y) + map2mineCount(map, x+1, y) + + map2mineCount(map, x-1, y+1) + map2mineCount(map, x, y+1) + map2mineCount(map, x+1, y+1) + } + ); + } + ``` + +3. Compilez le code Zokrates et créez la clé de vérification. La clé de vérification doit être créée avec la même entropie utilisée dans le serveur d'origine, [dans ce cas une chaîne vide](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L67). + + ```sh copy + zokrates compile --input dig.zok + zokrates setup -e "" + ``` + +4. Créez le vérificateur Solidity par vous-même, et vérifiez qu'il est fonctionnellement identique à celui sur la blockchain (le serveur ajoute un commentaire, mais ce n'est pas important). + + ```sh copy + zokrates export-verifier + diff verifier.sol ~/20240901-secret-state/packages/contracts/src/verifier.sol + ``` + +## Décisions de conception {#design} + +Dans toute application suffisamment complexe, il existe des objectifs de conception concurrents qui nécessitent des compromis. Examinons certains des compromis et pourquoi la solution actuelle est préférable à d'autres options. + +### Pourquoi la preuve à divulgation nulle de connaissance {#why-zero-knowledge} + +Pour un démineur, vous n'avez pas vraiment besoin de la preuve à divulgation nulle de connaissance. Le serveur peut toujours conserver la carte, puis simplement la révéler entièrement lorsque la partie est terminée. Ensuite, à la fin de la partie, le contrat intelligent peut calculer le hachage de la carte, vérifier qu'il correspond, et s'il ne correspond pas, pénaliser le serveur ou ignorer complètement la partie. + +Je n'ai pas utilisé cette solution plus simple car elle ne fonctionne que pour les jeux courts avec un état final bien défini. Quand un jeu est potentiellement infini (comme dans le cas des [mondes autonomes](https://0xparc.org/blog/autonomous-worlds)), vous avez besoin d'une solution qui prouve l'état _sans_ le révéler. + +En tant que tutoriel, cet article avait besoin d'un jeu court et facile à comprendre, mais cette technique est plus utile pour les jeux plus longs. + +### Pourquoi Zokrates ? {#why-zokrates} + +[Zokrates](https://zokrates.github.io/) n'est pas la seule bibliothèque de preuve à divulgation nulle de connaissance disponible, mais il est similaire à un langage de programmation normal et [impératif](https://en.wikipedia.org/wiki/Imperative_programming) et prend en charge les variables booléennes. + +Pour votre application, avec des exigences différentes, vous pourriez préférer utiliser [Circum](https://docs.circom.io/getting-started/installation/) ou [Cairo](https://www.cairo-lang.org/tutorials/getting-started-with-cairo/). + +### Quand compiler Zokrates {#when-compile-zokrates} + +Dans ce programme, nous compilons les programmes Zokrates [à chaque démarrage du serveur](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L60-L61). Il s'agit clairement d'un gaspillage de ressources, mais c'est un tutoriel, optimisé pour la simplicité. + +Si j'écrivais une application de niveau production, je vérifierais si j'ai un fichier avec les programmes Zokrates compilés pour cette taille de champ de mines, et si c'est le cas, je l'utiliserais. Il en va de même pour le déploiement d'un contrat de vérificateur en chaîne. + +### Création des clés de vérificateur et de prouveur {#key-creation} + +La [création de clés](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L63-L69) est un autre calcul pur qui n'a pas besoin d'être fait plus d'une fois pour une taille de champ de mines donnée. Encore une fois, cela n'est fait qu'une seule fois par souci de simplicité. + +De plus, nous pourrions utiliser [une cérémonie de configuration](https://zokrates.github.io/toolbox/trusted_setup.html#initializing-a-phase-2-ceremony). L'avantage d'une cérémonie de configuration est qu'il faut soit l'entropie, soit un résultat intermédiaire de chaque participant pour tricher sur la preuve à divulgation nulle de connaissance. Si au moins un participant à la cérémonie est honnête et supprime cette information, les preuves à divulgation nulle de connaissance sont à l'abri de certaines attaques. Cependant, il n'y a _aucun mécanisme_ pour vérifier que l'information a été supprimée de partout. Si les preuves à divulgation nulle de connaissance sont d'une importance capitale, vous voudrez participer à la cérémonie de configuration. + +Ici, nous nous appuyons sur les [pouvoirs perpétuels de tau](https://github.com/privacy-scaling-explorations/perpetualpowersoftau), qui ont eu des dizaines de participants. C'est probablement assez sûr, et beaucoup plus simple. Nous n'ajoutons pas non plus d'entropie lors de la création des clés, ce qui facilite la [vérification de la configuration de preuve à divulgation nulle de connaissance](#user-verify-zero-trust) par les utilisateurs. + +### Où vérifier {#where-verification} + +Nous pouvons vérifier les preuves à divulgation nulle de connaissance soit en chaîne (ce qui coûte du gaz), soit dans le client (en utilisant [`verify`](https://zokrates.github.io/toolbox/zokrates_js.html#verifyverificationkey-proof)). J'ai choisi la première option, car cela permet de [vérifier le vérificateur](#user-verify-zero-trust) une fois pour toutes, puis de faire confiance au fait qu'il ne change pas tant que l'adresse du contrat reste la même. Si la vérification était effectuée sur le client, vous devriez vérifier le code que vous recevez à chaque fois que vous téléchargez le client. + +De plus, bien que ce jeu soit solo, beaucoup de jeux sur la blockchain sont multijoueurs. La vérification en chaîne signifie que vous ne vérifiez la preuve à divulgation nulle de connaissance qu'une seule fois. Le faire dans le client exigerait que chaque client vérifie indépendamment. + +### Aplatir la carte en TypeScript ou en Zokrates ? {#where-flatten} + +En général, lorsque le traitement peut être effectué soit en TypeScript soit en Zokrates, il est préférable de le faire en TypeScript, qui est beaucoup plus rapide et ne nécessite pas de preuves à divulgation nulle de connaissance. C'est la raison, par exemple, pour laquelle nous ne fournissons pas à Zokrates le hachage pour qu'il vérifie qu'il est correct. Le hachage doit être fait à l'intérieur de Zokrates, mais la correspondance entre le hachage retourné et le hachage en chaîne peut se faire en dehors. + +Cependant, nous [aplatissons toujours la carte dans Zokrates](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L15-L20), alors que nous aurions pu le faire en TypeScript. La raison est que les autres options sont, à mon avis, pires. + +- Fournir un tableau unidimensionnel de booléens au code Zokrates, et utiliser une expression telle que `x*(height+2) + +y` pour obtenir la carte bidimensionnelle. Cela rendrait [le code](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/zero-knowledge.ts#L44-L47) quelque peu plus compliqué, j'ai donc décidé que le gain de performance ne valait pas la peine pour un tutoriel. + +- Envoyer à Zokrates à la fois le tableau unidimensionnel et le tableau bidimensionnel. Cependant, cette solution ne nous apporte rien. Le code Zokrates devrait vérifier que le tableau unidimensionnel qui lui est fourni est bien la représentation correcte du tableau bidimensionnel. Il n'y aurait donc aucun gain de performance. + +- Aplatir le tableau à deux dimensions dans Zokrates. C'est l'option la plus simple, donc je l'ai choisie. + +### Où stocker les cartes {#where-store-maps} + +Dans cette application, [`gamesInProgress`](https://github.com/qbzzt/20240901-secret-state/blob/main/packages/server/src/app.ts#L20) est simplement une variable en mémoire. Cela signifie que si votre serveur tombe en panne et doit être redémarré, toutes les informations qu'il stockait sont perdues. Non seulement les joueurs sont incapables de continuer leur partie, mais ils ne peuvent même pas en commencer une nouvelle car le composant en chaîne pense qu'ils ont toujours une partie en cours. + +C'est clairement une mauvaise conception pour un système de production, dans lequel vous stockeriez ces informations dans une base de données. La seule raison pour laquelle j'ai utilisé une variable ici est que c'est un tutoriel et que la simplicité est la principale considération. + +## Conclusion : Dans quelles conditions cette technique est-elle appropriée ? {#conclusion} + +Donc, vous savez maintenant comment écrire un jeu avec un serveur qui stocke un état secret qui n'a pas sa place sur la chaîne. Mais dans quels cas devriez-vous le faire ? Il y a deux considérations principales. + +- _Jeu de longue durée_ : [Comme mentionné ci-dessus](#why-zero-knowledge), dans un jeu court, vous pouvez simplement publier l'état une fois la partie terminée et faire tout vérifier à ce moment-là. Mais ce n'est pas une option lorsque le jeu dure longtemps ou indéfiniment, et que l'état doit rester secret. + +- _Une certaine centralisation acceptable_ : les preuves à divulgation nulle de connaissance peuvent vérifier l'intégrité, qu'une entité ne falsifie pas les résultats. Ce qu'ils ne peuvent pas faire, c'est garantir que l'entité sera toujours disponible et répondra aux messages. Dans les situations où la disponibilité doit également être décentralisée, les preuves à divulgation nulle de connaissance ne sont pas une solution suffisante, et vous avez besoin du [calcul multipartite](https://en.wikipedia.org/wiki/Secure_multi-party_computation). + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). + +### Remerciements {#acknowledgements} + +- Alvaro Alonso a lu une ébauche de cet article et a clarifié certaines de mes incompréhensions sur Zokrates. + +Toute erreur restante est de ma responsabilité.