From 80dea7a8d1d95bfc8cb9134a59ff7b89dfbfe4de Mon Sep 17 00:00:00 2001 From: Joshua <62268199+minimalsm@users.noreply.github.com> Date: Fri, 13 Feb 2026 23:51:57 +0000 Subject: [PATCH] i18n(fr): translation import part 07 of 13 (23 files) --- .../docs/standards/tokens/erc-777/index.md | 4 +- .../developers/docs/standards/tokens/index.md | 26 +- .../fr/developers/docs/storage/index.md | 94 +- .../fr/developers/docs/transactions/index.md | 121 +- .../fr/developers/docs/web2-vs-web3/index.md | 48 +- .../fr/developers/docs/wrapped-eth/index.md | 6 +- .../index.md | 187 +-- .../tutorials/all-you-can-cache/index.md | 357 +++-- .../developers/tutorials/app-plasma/index.md | 1261 +++++++++++++++++ .../index.md | 36 +- .../index.md | 585 ++++++++ .../index.md | 78 +- .../index.md | 372 +++++ .../index.md | 68 +- .../index.md | 60 +- .../erc-721-vyper-annotated-code/index.md | 445 +++--- .../tutorials/erc20-annotated-code/index.md | 572 +++++--- .../erc20-with-safety-rails/index.md | 118 +- .../tutorials/ethereum-for-web2-auth/index.md | 886 ++++++++++++ .../index.md | 98 +- .../index.md | 107 +- .../index.md | 939 +++++------- .../hello-world-smart-contract/index.md | 206 ++- 23 files changed, 4815 insertions(+), 1859 deletions(-) create mode 100644 public/content/translations/fr/developers/tutorials/app-plasma/index.md create mode 100644 public/content/translations/fr/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md create mode 100644 public/content/translations/fr/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md create mode 100644 public/content/translations/fr/developers/tutorials/ethereum-for-web2-auth/index.md diff --git a/public/content/translations/fr/developers/docs/standards/tokens/erc-777/index.md b/public/content/translations/fr/developers/docs/standards/tokens/erc-777/index.md index 2582c95635e..c6cb9247cea 100644 --- a/public/content/translations/fr/developers/docs/standards/tokens/erc-777/index.md +++ b/public/content/translations/fr/developers/docs/standards/tokens/erc-777/index.md @@ -1,6 +1,6 @@ --- title: Norme de jeton ERC-777 -description: +description: "Découvrez ERC-777, une norme de jeton fongible améliorée avec des crochets, bien qu'ERC-20 soit recommandé pour des raisons de sécurité." lang: fr --- @@ -42,4 +42,4 @@ Les contrats ERC-777 peuvent interagir comme s'il s'agissait de contrats ERC-20. ## En savoir plus {#further-reading} -[EIP-777 : Jeton standard] (https://eips.ethereum.org/EIPS/eip-777) +[EIP-777 : Jeton standard](https://eips.ethereum.org/EIPS/eip-777) diff --git a/public/content/translations/fr/developers/docs/standards/tokens/index.md b/public/content/translations/fr/developers/docs/standards/tokens/index.md index e20901a4082..6d907c310ae 100644 --- a/public/content/translations/fr/developers/docs/standards/tokens/index.md +++ b/public/content/translations/fr/developers/docs/standards/tokens/index.md @@ -1,39 +1,41 @@ --- title: Normes de jetons -description: +description: Explorez les normes de jetons Ethereum, y compris ERC-20, ERC-721 et ERC-1155 pour les jetons fongibles et non fongibles. lang: fr incomplete: true --- ## Introduction {#introduction} -De nombreuses normes de développement Ethereum se concentrent sur les interfaces de jetons. Ces normes aident à garantir que les contrats intelligents restent composables, afin que, par exemple, lorsqu'un nouveau projet émet un jeton, celui-ci reste compatible avec les échanges décentralisés existants. +De nombreuses normes de développement Ethereum se concentrent sur les interfaces de jetons. Ces normes contribuent à garantir que les contrats intelligents restent composables, de sorte que lorsqu'un nouveau projet émet un jeton, celui-ci reste compatible avec les échanges décentralisés et les applications existants. + +Les normes de jetons définissent la manière dont les jetons se comportent et interagissent dans l'écosystème Ethereum. Elles permettent aux développeurs de créer plus facilement sans réinventer la roue, en garantissant que les jetons fonctionnent de manière transparente avec les portefeuilles, les échanges et les plateformes DeFi. Que ce soit dans les jeux, la gouvernance ou d'autres cas d'utilisation, ces normes assurent la cohérence et rendent Ethereum plus interconnecté. ## Prérequis {#prerequisites} -- [Normes de développement Ethereum](/developers/docs/standards/) +- [Normes de développement d'Ethereum](/developers/docs/standards/) - [Contrats intelligents](/developers/docs/smart-contracts/) -## Normes des jetons {#token-standards} +## Normes de jetons {#token-standards} Voici quelques-unes des normes de jetons les plus populaires sur Ethereum : -- [ERC-20](/developers/docs/standards/tokens/erc-20/) - Une interface type pour les jetons fongibles (interchangeables) comme les jetons de vote, les jetons d'enjeu ou les monnaies virtuelles. +- [ERC-20](/developers/docs/standards/tokens/erc-20/) - Une interface standard pour les jetons fongibles (interchangeables), comme les jetons de vote, les jetons de staking ou les monnaies virtuelles. ### Normes NFT {#nft-standards} -- [ERC-721](/developers/docs/standards/tokens/erc-721/) - Une interface type pour les jetons non fongibles, comme ceux requis pour les œuvres d'art ou une chanson. -- [ERC-1155](/developers/docs/standards/tokens/erc-1155/) - Permet des transactions et des regroupements de transactions plus efficaces – réduisant ainsi les coûts. Cette norme de jeton permet de créer à la fois des jetons utilitaires (comme $BNB ou $BAT) et des jetons non fongibles comme les CryptoPunks. +- [ERC-721](/developers/docs/standards/tokens/erc-721/) - Une interface standard pour les jetons non fongibles (NFT), comme un titre de propriété pour une œuvre d'art ou une chanson. +- [ERC-1155](/developers/docs/standards/tokens/erc-1155/) - L'ERC-1155 permet des échanges plus efficaces et le regroupement des transactions, réduisant ainsi les coûts. Cette norme de jeton permet de créer à la fois des jetons utilitaires (comme $BNB ou $BAT) et des jetons non fongibles comme les CryptoPunks. La liste complète des propositions [ERC](https://eips.ethereum.org/erc). -## Complément d'information {#further-reading} +## En savoir plus {#further-reading} _Une ressource communautaire vous a aidé ? Modifiez cette page et ajoutez-la !_ ## Tutoriels connexes {#related-tutorials} -- [Liste de contrôle d'intégration de jetons](/developers/tutorials/token-integration-checklist/) _- Liste de contrôle des choses à prendre en compte quand on interagit avec des jetons._ -- [Comprendre le contrat intelligent de jeton ERC-20](/developers/tutorials/understand-the-erc-20-token-smart-contract/) _- Introduction au déploiement de votre premier contrat intelligent sur un réseau de test Ethereum._ -- [Transférer et approuver des jetons ERC20 depuis un contrat intelligent Solidity](/developers/tutorials/transfers-and-approval-of-erc-20-tokens-from-a-solidity-smart-contract/) _– Comment utiliser un contrat intelligent pour interagir avec un jeton en utilisant le langage Solidity._ -- [Implémenter un marché ERC721 [guide pratique]](/developers/tutorials/how-to-implement-an-erc721-market/) _- Comment mettre à la vente des objets jetonnés sur une planche de classification décentralisée._ +- [Liste de contrôle d'intégration de jetons](/developers/tutorials/token-integration-checklist/) _– Une liste de contrôle des éléments à prendre en compte lors de l'interaction avec des jetons._ +- [Comprendre le contrat intelligent de jeton ERC-20](/developers/tutorials/understand-the-erc-20-token-smart-contract/) _– Une introduction au déploiement de votre premier contrat intelligent sur un réseau de test Ethereum._ +- [Transferts et approbation de jetons ERC-20 à partir d'un contrat intelligent Solidity](/developers/tutorials/transfers-and-approval-of-erc-20-tokens-from-a-solidity-smart-contract/) _– Comment utiliser un contrat intelligent pour interagir avec un jeton à l'aide du langage Solidity._ +- [Mettre en œuvre un marché ERC-721 [guide pratique]](/developers/tutorials/how-to-implement-an-erc721-market/) _– Comment mettre en vente des articles jetonisés sur un tableau d'annonces décentralisé._ diff --git a/public/content/translations/fr/developers/docs/storage/index.md b/public/content/translations/fr/developers/docs/storage/index.md index c3833772c72..a2372d0e932 100644 --- a/public/content/translations/fr/developers/docs/storage/index.md +++ b/public/content/translations/fr/developers/docs/storage/index.md @@ -1,31 +1,31 @@ --- -title: Stockage décentralisé -description: Présentation du stockage décentralisé et des outils disponibles à intégrer dans une dApp. +title: "Stockage décentralisé" +description: "Présentation du stockage décentralisé et des outils disponibles à intégrer dans une dApp." lang: fr --- Contrairement à un serveur centralisé exploité par une entreprise ou organisation unique, les systèmes de stockage décentralisé se composent d'un réseau de pair à pair d'opérateurs-utilisateurs qui détiennent une partie de l'ensemble des données, créant ainsi un système de partage de fichiers résiliant. Cela peut être via une application basée sur la blockchain ou bien sur n'importe quel réseau basé sur le principe du pair à pair. -Ethereum lui-même peut être utilisé comme un système de stockage décentralisé, c'est d'ailleurs déjà le cas concernant le stockage de code compris dans tous les contrats intelligent. Cependant, lorsqu'il s'agit de grandes quantités de données, Ethereum n'a été conçu pour cela. La chaîne ne cesse de croître, mais au moment d'écrire ces lignes, la chaîne Ethereum est d'environ 500 Go à 1 To ([selon le client](https://etherscan.io/chartsync/chaindefault)), et chaque nœud du réseau doit être en mesure de stocker toutes les données. Si la chaîne devait s'étendre à de grandes quantités de données (disons 5 To par exemple), il serait impossible pour tous les nœuds de continuer à fonctionner. En outre, le coût du déploiement d'une telle quantité de données sur le réseau principal serait prohibitif en raison des frais de [gaz](/developers/docs/gas). +Ethereum lui-même peut être utilisé comme un système de stockage décentralisé, c'est d'ailleurs déjà le cas concernant le stockage de code compris dans tous les contrats intelligent. Cependant, lorsqu'il s'agit de grandes quantités de données, Ethereum n'a été conçu pour cela. La chaîne ne cesse de croître, mais au moment d'écrire ces lignes, la chaîne Ethereum est d'environ 500 Go à 1 To ([selon le client](https://etherscan.io/chartsync/chaindefault)), et chaque nœud du réseau doit être en mesure de stocker toutes les données. Si la chaîne devait s'étendre à de grandes quantités de données (disons 5 To par exemple), il serait impossible pour tous les nœuds de continuer à fonctionner. De plus, le coût du déploiement d'une telle quantité de données sur le réseau principal serait prohibitif en raison des frais de [gaz](/developers/docs/gas). En raison de ces contraintes, nous avons besoin d'une chaîne ou d'une méthodologie différente pour stocker de grandes quantités de données de manière décentralisée. En se penchant sur la question des options de stockage décentralisé (dStorage), il y a des choses qu'un utilisateur doit garder à l'esprit. -- Mécanisme de persistance / structure d'incitation +- Mécanisme de persistance / structure incitative - Application de conservation des données -- Décentralisé +- Décentralisation - Consensus -## Mécanisme de persistance / structure incitative {#persistence-mechanism} +## Mécanisme de persistance / Structure d'incitation {#persistence-mechanism} -### Orientation blockchain {#blockchain-based} +### Basé sur la blockchain {#blockchain-based} Pour qu'une donnée persiste indéfiniment, nous devons utiliser un mécanisme de persistance. Par exemple sur Ethereum, le mécanisme de persistance réside dans le fait que toute la chaîne doit être prise en compte lors de l'exécution d'un nœud. De nouvelles données sont traitées en bout de chaîne et elle ne cesse donc de croître, exigeant que chaque nœud reproduise toutes les données embarquées. -Ce processus est connu sous le nom de : persistance **basée sur la blockchain**. +C'est ce qu'on appelle la persistance **basée sur la blockchain**. -Le problème avec la persistance basée sur la blockchain est que la chaîne pourrait devenir beaucoup trop grande pour entretenir et stocker toutes les données (ex. [de nombreuses sources](https://healthit.com.au/how-big-is-the-internet-and-how-do-we-measure-it/) estiment que l'Internet nécessite plus de 40 Zetabytes de capacité de stockage). +Le problème avec la persistance basée sur la blockchain est que la chaîne pourrait devenir beaucoup trop volumineuse pour maintenir et stocker toutes les données de manière réalisable (par exemple, [de nombreuses sources](https://healthit.com.au/how-big-is-the-internet-and-how-do-we-measure-it/) estiment qu'Internet nécessite plus de 40 zettaoctets de capacité de stockage). La blockchain doit également avoir une certaine structure incitative. Pour la persistance basée sur la blockchain, il y a un paiement effectué au validateur. Les validateurs sont payés pour ajouter les données lorsqu'elles sont ajoutées à la chaine. @@ -34,23 +34,23 @@ Plateformes avec persistance basée sur la blockchain : - Ethereum - [Arweave](https://www.arweave.org/) -### Orientation contrat {#contract-based} +### Basé sur les contrats {#contract-based} -**La persistance orientée contrat** a l'intuition que les données ne peuvent pas être reproduites par chaque nœud et stockées pour toujours et au lieu de cela, il doit alors être gardé à jour avec des accords contractuels. Ce sont des accords conclus avec plusieurs nœuds qui promettent de conserver une partie de données pendant une certaine période. Ils doivent être remboursés ou renouvelés chaque fois qu'ils sont exécutés pour conserver les données. +La persistance **basée sur les contrats** repose sur l'intuition que les données ne peuvent pas être répliquées par chaque nœud et stockées éternellement, et qu'elles doivent plutôt être maintenues par des accords contractuels. Ce sont des accords conclus avec plusieurs nœuds qui promettent de conserver une partie de données pendant une certaine période. Ils doivent être remboursés ou renouvelés chaque fois qu'ils sont exécutés pour conserver les données. Dans la plupart des cas, au lieu de stocker toutes les données en chaîne, le hachage de l'endroit où les données se trouvent sur une chaîne est stocké. Ainsi, l'ensemble de la chaîne n'a pas besoin d'évoluer pour conserver toutes les données. Les plateformes avec persistance basée sur contrat : -- [Filecoin](https://docs.filecoin.io/about-filecoin/what-is-filecoin/) -- [Skynet](https://siasky.net/) +- [Filecoin](https://docs.filecoin.io/basics/what-is-filecoin) +- [Skynet](https://sia.tech/) - [Storj](https://storj.io/) - [Züs](https://zus.network/) -- [Réseau Crust](https://crust.network) +- [Crust Network](https://crust.network) - [Swarm](https://www.ethswarm.org/) - [4EVERLAND](https://www.4everland.org/) -### Autres considérations {#additional-consideration} +### Considérations supplémentaires {#additional-consideration} IPFS est un système distribué pour stocker et accéder aux fichiers, sites Web, applications et données. Il ne dispose pas d'un système d'incitation intégré, mais peut être utilisé avec n'importe quelle solution d'incitation orientée contrat ci-dessus pour une persistance à plus long terme. Une autre façon de maintenir les données sur IPFS est de travailler avec un service d'alerte, qui va « épingler » vos données pour vous. Vous pouvez même exécuter votre propre nœud IPFS et contribuer au réseau pour persister gratuitement vos données et/ou celles d'autres ! @@ -58,18 +58,18 @@ IPFS est un système distribué pour stocker et accéder aux fichiers, sites Web - [Pinata](https://www.pinata.cloud/) _(service d'épinglage IPFS)_ - [web3.storage](https://web3.storage/) _(service d'épinglage IPFS/Filecoin)_ - [Infura](https://infura.io/product/ipfs) _(service d'épinglage IPFS)_ -- [IPFS Scan](https://ipfs-scan.io) _(explorateur IPFS pinning)_ -- [4EVERLAND](https://www.4everland.org/)_ (service d'épinglage IPFS) _ +- [IPFS Scan](https://ipfs-scan.io) _(explorateur d'épinglage IPFS)_ +- [4EVERLAND](https://www.4everland.org/) _(service d'épinglage IPFS)_ - [Filebase](https://filebase.com) _(Service d'épinglage IPFS)_ - [Spheron Network](https://spheron.network/) _(service d'épinglage IPFS/Filecoin)_ SWARM est une technologie décentralisée de stockage et de distribution de données avec un système incitatif de stockage et un prix de location de stockage oracle. -## Conservation des données {#data-retention} +## Rétention des données {#data-retention} Afin de conserver des données, les systèmes doivent disposer d'une sorte de mécanisme pour s'assurer que les données sont bien conservées. -### Mécanisme de défi {#challenge-mechanism} +### Mécanisme de contestation {#challenge-mechanism} Un des moyens les plus populaires pour s'assurer que les données sont conservées, est d'utiliser un certain type de défi cryptographique à relever par les nœuds afin de s'assurer qu'ils disposent toujours des données. Un simple moyen est de regarder la preuve d'accès d'Arweav. Ils lancent un défi aux nœuds pour voir s'ils disposent des données du bloc le plus récent et d'un bloc aléatoire dans le passé. Si le nœud ne peut pas trouver la réponse, il est pénalisé. @@ -98,7 +98,7 @@ Outils décentralisés sans KYC : ### Consensus {#consensus} -La plupart de ces outils ont leur propre version de [mécanisme de consensus](/developers/docs/consensus-mechanisms/) mais généralement ils sont basés soit sur une [**Preuve de travail (PoW)**](/developers/docs/consensus-mechanisms/pow/) soit sur une [**Preuve d'enjeu (PoS)**](/developers/docs/consensus-mechanisms/pos/). +La plupart de ces outils ont leur propre version d'un [mécanisme de consensus](/developers/docs/consensus-mechanisms/), mais ils sont généralement basés soit sur la [**preuve de travail (PoW)**](/developers/docs/consensus-mechanisms/pow/), soit sur la [**preuve d'enjeu (PoS)**](/developers/docs/consensus-mechanisms/pos/). Preuve de travail (PoW) : @@ -114,103 +114,103 @@ Preuve d'enjeu (PoS) : ## Outils connexes {#related-tools} -**IPFS - _Le système de fichier InterPlanetary est un système de stockage décentralisé et de référencement de fichiers pour Ethereum_** +**IPFS – _InterPlanetary File System est un système de stockage et de référencement de fichiers décentralisé pour Ethereum._** - [Ipfs.io](https://ipfs.io/) - [Documentation](https://docs.ipfs.io/) - [GitHub](https://github.com/ipfs/ipfs) -**Storj DCS - _Stockage décentralisé d'objet cloud sécurisé, privé et compatible S3 pour les développeurs._** +**Storj DCS – _Stockage d'objets cloud décentralisé, sécurisé, privé et compatible S3 pour les développeurs._** - [Storj.io](https://storj.io/) - [Documentation](https://docs.storj.io/) - [GitHub](https://github.com/storj/storj) -**Skynet - _Skynet est une chaîne PoW décentralisée dédiée à un Web décentralisé._** +**Sia - _Utilise la cryptographie pour créer un marché de stockage cloud sans nécessité de confiance, permettant aux acheteurs et vendeurs d'échanger directement._** -- [Skynet.net](https://siasky.net/) -- [Documentation](https://siasky.net/docs/) -- [GitHub](https://github.com/SkynetLabs/) +- [Skynet.net](https://sia.tech/) +- [Documentation](https://docs.sia.tech/) +- [GitHub](https://github.com/SiaFoundation/) -**Filecoin - _Filecoin a été créé par la même équipe derrière IPFS. C'est une couche d'incitation au sommet des idéaux IPFS._** +**Filecoin - _Filecoin a été créé par la même équipe à l'origine d'IPFS._** C'est une couche d'incitation au sommet des idéaux IPFS._\*\* - [Filecoin.io](https://filecoin.io/) - [Documentation](https://docs.filecoin.io/) - [GitHub](https://github.com/filecoin-project/) -**Arweave - _Arweave est une plateforme dStorage pour stocker des données._** +**Arweave - _Arweave est une plateforme dStorage pour le stockage de données._** - [Arweave.org](https://www.arweave.org/) - [Documentation](https://docs.arweave.org/info/) - [Arweave](https://github.com/ArweaveTeam/arweave/) -**Züs - _Züs est une plateforme dStorage basée sur la Preuve d'enjeu avec des fragments et des blobbers._** +**Züs – _Züs est une plateforme dStorage à preuve d'enjeu avec fragmentation et « blobbers »._** - [zus.network](https://zus.network/) -- [Documentation](https://0chaindocs.gitbook.io/zus-docs) +- [Documentation](https://docs.zus.network/zus-docs/) - [GitHub](https://github.com/0chain/) -**Réseau Crust - _Crust est une plateforme dStorage basée sur IPFS_** +**Crust Network – _Crust est une plateforme dStorage qui s'appuie sur IPFS._** - [Crust.network](https://crust.network) - [Documentation](https://wiki.crust.network) - [GitHub](https://github.com/crustio) -**Swarm - _Plateforme de stockage distribuée et service de distribution de contenu pour la pile web3 Ethereum_** +**Swarm – _Une plateforme de stockage distribuée et un service de distribution de contenu pour la pile web3 d'Ethereum._** - [EthSwarm.org](https://www.ethswarm.org/) -- [Documentation](https://docs.ethswarm.org/docs/) +- [Documentation](https://docs.ethswarm.org/) - [GitHub](https://github.com/ethersphere/) -**OrbitDB - _Base de données décentralisée P2P construite sur IPFS_** +**OrbitDB – _Une base de données décentralisée pair-à-pair (P2P) qui s'appuie sur IPFS._** - [OrbitDB.org](https://orbitdb.org/) - [Documentation](https://github.com/orbitdb/field-manual/) - [GitHub](https://github.com/orbitdb/orbit-db/) -**Aleph.im - _Projet cloud décentralisé (base de données, stockage de fichiers, calcul et DID). Un mélange unique de technologie hors chaîne et en chaîne P2P. Compatibilité avec IPFS et multi-chaînes._** +**Aleph.im - _Projet de cloud décentralisé (base de données, stockage de fichiers, calcul et DID)._** Un mélange unique de technologie hors chaîne et en chaîne P2P. Compatibilité avec IPFS et multi-chaînes._\*\* -- [Aleph.im](https://aleph.im/) -- [Documentation](https://aleph.im/#/developers/) +- [Aleph.im](https://aleph.cloud/) +- [Documentation](https://docs.aleph.cloud/) - [GitHub](https://github.com/aleph-im/) -**Ceramic - _Stockage de base de données IPFS contrôlé par l'utilisateur pour des applications riches en données et engageantes._** +**Ceramic – _Stockage de base de données IPFS contrôlé par l'utilisateur pour des applications riches en données et attrayantes._** - [Ceramic.network](https://ceramic.network/) -- [Documentation](https://developers.ceramic.network/learn/welcome/) +- [Documentation](https://developers.ceramic.network/) - [GitHub](https://github.com/ceramicnetwork/js-ceramic/) -**File base - _Stockage décentralisé compatible S3 et service d'épinglage IPFS géo-redondant. Tous les fichiers téléchargés sur IPFS via Filebase sont automatiquement épinglés à l'infrastructure Filebase avec une réplication 3x à travers le monde._** +**Filebase - _Stockage décentralisé compatible S3 et service d'épinglage IPFS géo-redondant._** Tous les fichiers téléversés sur IPFS via Filebase sont automatiquement épinglés à l'infrastructure Filebase avec une triple réplication à travers le monde._\*\* - [Filebase.com](https://filebase.com/) - [Documentation](https://docs.filebase.com/) - [GitHub](https://github.com/filebase) -**4EVERLAND - _Une plateforme de calcul cloud Web 3 qui intègre les capacités de stockage, de calcul et de réseautage de base, est compatible S3 et fournit un stockage de données synchronisé sur les réseaux de stockage décentralisés tels que IPFS et Arweave._** +**4EVERLAND - _Une plateforme de cloud computing Web 3.0 qui intègre des fonctionnalités de base de stockage, de calcul et de réseau, est compatible S3 et fournit un stockage de données synchrone sur des réseaux de stockage décentralisés tels qu'IPFS et Arweave._** - [4everland.org](https://www.4everland.org/) - [Documentation](https://docs.4everland.org/) - [GitHub](https://github.com/4everland) -**Kaleido - _Une plateforme blockchain-as-a-service avec un bouton clic IPFS Nodes_** +**Kaleido - _Une plateforme « blockchain-as-a-service » avec des nœuds IPFS sur simple clic_** - [Kaleido](https://kaleido.io/) - [Documentation](https://docs.kaleido.io/kaleido-services/ipfs/) - [GitHub](https://github.com/kaleido-io) -**Spheron Network - _Spheron est une plateforme-en-tant-que-service (PaaS) conçue pour les dApps cherchant à lancer leurs applications sur un infra décentralisé avec les meilleures performances. Elle fournit des capacités de calcul, de stockage décentralisé, CDN & de l'hébergement web prêt à l'emploi._** +**Spheron Network - _Spheron est une plateforme en tant que service (PaaS) conçue pour les dApps qui cherchent à lancer leurs applications sur une infrastructure décentralisée avec des performances optimales._** Elle fournit des capacités de calcul, un stockage décentralisé, un CDN et un hébergement web prêts à l'emploi._\*\* - [spheron.network](https://spheron.network/) - [Documentation](https://docs.spheron.network/) - [GitHub](https://github.com/spheronFdn) -## Complément d'information {#further-reading} +## En savoir plus {#further-reading} -- [Qu'est-ce qu'un stockage décentralisé ?](https://coinmarketcap.com/alexandria/article/what-is-decentralized-storage-a-deep-dive-by-filecoin) - _CoinMarketCap_ -- [Cinq mythes répandus à propos du stockage décentralisé](https://www.storj.io/blog/busting-five-common-myths-about-decentralized-storage) - _Storj_ +- [Qu'est-ce que le stockage décentralisé ?](https://coinmarketcap.com/academy/article/what-is-decentralized-storage-a-deep-dive-by-filecoin) - _CoinMarketCap_ +- [Cinq mythes courants sur le stockage décentralisé démystifiés](https://www.storj.io/blog/busting-five-common-myths-about-decentralized-storage) - _Storj_ _Une ressource communautaire vous a aidé ? Modifiez cette page et ajoutez-la !_ ## Sujets connexes {#related-topics} -- [Infrastructures de développement](/developers/docs/frameworks/) +- [Frameworks de développement](/developers/docs/frameworks/) diff --git a/public/content/translations/fr/developers/docs/transactions/index.md b/public/content/translations/fr/developers/docs/transactions/index.md index 9fbfe0ec72e..b2dda281c81 100644 --- a/public/content/translations/fr/developers/docs/transactions/index.md +++ b/public/content/translations/fr/developers/docs/transactions/index.md @@ -1,20 +1,21 @@ --- title: Transactions -description: 'Présentation des transactions Ethereum : leur fonctionnement, leur structure de données et comment les envoyer via une application.' +description: "Présentation des transactions Ethereum : leur fonctionnement, leur structure de données et comment les envoyer via une application." lang: fr --- -Les transactions sont des instructions signées cryptographiquement depuis des comptes. Un compte va initier une transaction pour mettre à jour l'état du réseau Ethereum. La transaction la plus simple consiste à transférer de l'ETH d'un compte à un autre. +Les transactions sont des instructions signées cryptographiquement provenant des comptes. Un compte va initier une transaction pour mettre à jour l'état du réseau Ethereum. La transaction la plus simple consiste à transférer de l'ETH d'un compte à un autre. ## Prérequis {#prerequisites} -Pour vous aider à mieux comprendre cet article, nous vous recommandons de commencer par lire les pages [Comptes](/developers/docs/accounts/) et notre [Introduction à Ethereum](/developers/docs/intro-to-ethereum/). +Pour vous aider à mieux comprendre cette page, nous vous recommandons de lire d'abord [Comptes](/developers/docs/accounts/) et notre [introduction à Ethereum](/developers/docs/intro-to-ethereum/). ## Qu'est-ce qu'une transaction ? {#whats-a-transaction} Une transaction Ethereum est une action initiée par un compte externe, c'est-à-dire un compte géré par un être humain et non par un contrat. Par exemple, si Marc envoie 1 ETH à Alice, le compte de Marc doit être débité et celui d'Alice doit être crédité. Cette action, qui modifie l'état, se produit dans le cadre d'une transaction. -![Diagramme montrant un changement d'état de cause de la transaction](./tx.png) _Schéma adapté à partir du document [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagramme montrant une transaction qui entraîne un changement d'état](./tx.png) +_Diagramme adapté de [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Les transactions, qui modifient l'état de l'EVM, doivent être diffusées sur l'ensemble du réseau. N'importe quel nœud peut diffuser une demande pour qu'une transaction soit exécutée sur l'EVM. Un validateur exécutera ensuite la transaction et diffusera au reste du réseau le changement d'état qui en résultera. @@ -22,17 +23,17 @@ Les transactions requièrent des frais et doivent être incluses dans un bloc va Une transaction soumise comprend les informations suivantes : -- `depuis` - l'adresse de l'expéditeur qui signera la transaction. Il s'agira d'un compte externe, car les comptes contractuels ne vous permettront pas d'envoyer des transactions -- `to` : l'adresse de réception (S'il s'agit d'un compte externe, la transaction va transférer la valeur. S'il s'agit d'un compte de contrat, la transaction exécutera le code du contrat.) -- `signature` : identifiant de l'expéditeur. Cette signature est générée lorsque la clé privée de l'expéditeur signe la transaction, et confirme que l'expéditeur a autorisé cette transaction. -- `nonce` -, il s'agit d'une machine à travers laquelle un nombre maximum d'essais consécutifs est réalisé, il qualifie aussi le numéro de transactions dans la liste des transactions sortantes depuis votre adresse -- `valeur` - montants de l'Ether (ETH) à transférer de l'expéditeur au destinataire (libellé en WEI parallèlement à la valeur de l'Ether, qui atteint les 1e+18wei) -- `input data` – champ facultatif permettant d'inclure des données arbitraires -- `gasLimit` : Quantité maximum d’unités de gaz pouvant être consommée par la transaction. La [machine virtuelle d'Ethereum (EVM)](/developers/docs/evm/opcodes) donne une estimation de la quantité de gaz (unité virtuelle) nécessaire à une opération, ce qui permet de représenter les coûts informatiques d'une opération sur le réseau -- `maxPriorityFeePerGas` : la quantité maximale de gaz à inclure comme pourboire pour le validateur -- `maxFeePerGas` : montant maximum de gaz prêt à être payé pour la transaction (y compris `baseFeePerGas` et `maxPriorityFeePerGas`) +- `from` – l'adresse de l'expéditeur, qui signera la transaction. Il s'agira d'un compte externe, car les comptes contractuels ne vous permettront pas d'envoyer des transactions +- `to` – l'adresse de réception (s'il s'agit d'un compte externe, la transaction transférera de la valeur). S'il s'agit d'un compte de contrat, la transaction exécutera le code du contrat.) +- `signature` – l'identifiant de l'expéditeur. Cette signature est générée lorsque la clé privée de l'expéditeur signe la transaction, et confirme que l'expéditeur a autorisé cette transaction. +- `nonce` - un compteur à incrémentation séquentielle qui indique le numéro de la transaction du compte +- `value` – montant d'ETH à transférer de l'expéditeur au destinataire (exprimé en WEI, où 1 ETH équivaut à 1e+18 wei) +- `input data` – champ facultatif pour inclure des données arbitraires +- `gasLimit` – la quantité maximale d'unités de gaz qui peut être consommée par la transaction. L'[EVM](/developers/docs/evm/opcodes) spécifie les unités de gaz requises par chaque étape de calcul +- `maxPriorityFeePerGas` - le prix maximum du gaz consommé à inclure comme pourboire pour le validateur +- `maxFeePerGas` - les frais maximum par unité de gaz que l'on est prêt à payer pour la transaction (incluant `baseFeePerGas` et `maxPriorityFeePerGas`) -Le gaz est une référence au calcul nécessaire au traitement de la transaction par un validateur. Les utilisateurs doivent payer des frais pour ce calcul. `gasLimit` et `gasPrice` déterminent les frais de transaction maximum payés au validateur. [Plus d'infos sur le gaz](/developers/docs/gas/) +Le gaz est une référence au calcul nécessaire au traitement de la transaction par un validateur. Les utilisateurs doivent payer des frais pour ce calcul. Les paramètres `gasLimit` et `maxPriorityFeePerGas` déterminent les frais de transaction maximums payés au validateur. [En savoir plus sur le gaz](/developers/docs/gas/). L'objet de transaction ressemblera un peu à ceci : @@ -41,10 +42,10 @@ L'objet de transaction ressemblera un peu à ceci : from: "0xEA674fdDe714fd979de3EdF0F56AA9716B898ec8", to: "0xac03bb73b6a9e108530aff4df5077c2b3d481e5a", gasLimit: "21000", - maxFeePerGas: "300" - maxPriorityFeePerGas: "10" + maxFeePerGas: "300", + maxPriorityFeePerGas: "10", nonce: "0", - value: "10000000000", + value: "10000000000" } ``` @@ -52,7 +53,7 @@ Un objet de transaction doit être signé avec la clé privée de l'expéditeur. Un client Ethereum comme Geth gérera ce processus de signature. -Exemple d'appel [JSON-RPC](/developers/docs/apis/json-rpc) : +Exemple d'appel [JSON-RPC](/developers/docs/apis/json-rpc) : ```json { @@ -99,22 +100,26 @@ Exemple de réponse : } ``` -- `raw` est la transaction signée sous forme de [préfixe de longueur récursive (RLP)](/developers/docs/data-structures-and-encoding/rlp) -- `tx` est la transaction signée sous la forme JSON +- le `raw` est la transaction signée sous forme codée en [Préfixe de longueur récursive (RLP)](/developers/docs/data-structures-and-encoding/rlp) +- le `tx` est la transaction signée sous forme JSON Grâce au hachage de la signature, il est possible de prouver de façon cryptographique que la transaction provient de l'expéditeur et qu'elle a été soumise au réseau. ### Le champ de données {#the-data-field} -La grande majorité des transactions accèdent à un contrat provenant d'un compte externe. La plupart des contrats sont écrits en Solidity et interprètent leur champ de données conformément à l'[interface binaire-programme (ABI)](/glossary/#abi). +La grande majorité des transactions accèdent à un contrat provenant d'un compte externe. +La plupart des contrats sont écrits en Solidity et interprètent leur champ de données conformément à l'[interface binaire d'application (ABI)](/glossary/#abi). -Les quatre premiers octets indiquent la fonction à appeler, en utilisant les hachages de son nom et de ses arguments. Vous pouvez parfois identifier la fonction depuis le sélecteur à l'aide de [cette base de données](https://www.4byte.directory/signatures/). +Les quatre premiers octets indiquent la fonction à appeler, en utilisant les hachages de son nom et de ses arguments. +Vous pouvez parfois identifier la fonction à partir du sélecteur en utilisant [cette base de données](https://www.4byte.directory/signatures/). -Le reste des calldata est constitué des arguments, [encodés comme indiqué dans les spécifications ABI](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). +Le reste des données d'appel (calldata) correspond aux arguments, [codés comme spécifié dans les spécifications de l'ABI](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). -Par exemple, prenons [cette transaction](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). Utilisez **Cliquer pour en voir plus** pour voir les calldata. +Par exemple, regardons [cette transaction](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). +Utilisez **Cliquer pour en voir plus** pour voir les données d'appel (calldata). -Le sélecteur de fonction est `0xa9059cbb`. Il existe plusieurs [fonctions connues avec cette signature](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). Dans ce cas, [le code source du contrat](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) a été chargé sur Etherscan, nous savons donc que la fonction est `transfer(address,uint256)`. +Le sélecteur de fonction est `0xa9059cbb`. Il existe plusieurs [fonctions connues avec cette signature](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). +Dans ce cas [le code source du contrat](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) a été téléversé sur Etherscan, nous savons donc que la fonction est `transfer(address,uint256)`. Le reste des données est : @@ -123,9 +128,11 @@ Le reste des données est : 000000000000000000000000000000000000000000000000000000003b0559f4 ``` -Selon les spécifications ABI, les valeurs entières (comme les adresses, qui sont des entiers de 20 octets) apparaissent dans l'ABI sous forme de mots de 32 octets, complétées de zéros au début. Nous savons donc que l'adresse `to` est [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279). La valeur `value` est 0x3b0559f4 = 990206452. +Selon les spécifications ABI, les valeurs entières (comme les adresses, qui sont des entiers de 20 octets) apparaissent dans l'ABI sous forme de mots de 32 octets, complétées de zéros au début. +Nous savons donc que l'adresse `to` est [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279). +La `value` est 0x3b0559f4 = 990206452. -## Type de transaction {#types-of-transactions} +## Types de transactions {#types-of-transactions} Sur Ethereum, il existe plusieurs types de transactions : @@ -135,9 +142,9 @@ Sur Ethereum, il existe plusieurs types de transactions : ### À propos du gaz {#on-gas} -Comme mentionné, les transactions ont un coût en [gaz](/developers/docs/gas/) pour être exécutées. Les transactions simples de transfert requièrent 21 000 unités de gaz. +Comme mentionné, les transactions coûtent du [gaz](/developers/docs/gas/) pour être exécutées. Les transactions simples de transfert requièrent 21 000 unités de gaz. -Ainsi, pour envoyer 1 ETH à Alice avec un `baseFeePerGas` de 190 gwei et `maxPriorityFeePerGas` de 10 gwei, Bob devra payer les frais suivants : +Donc, pour que Bob envoie 1 ETH à Alice avec un `baseFeePerGas` de 190 gwei et un `maxPriorityFeePerGas` de 10 gwei, Bob devra payer les frais suivants : ``` (190 + 10) * 21000 = 4 200 000 gwei @@ -145,77 +152,83 @@ Ainsi, pour envoyer 1 ETH à Alice avec un `baseFeePerGas` de 190 gwei et `maxPr 0,0042 ETH ``` -Le compte de Bob sera débité de **-1,0042 ETH** (1 ETH pour Alice + 0,0042 ETH en frais de gaz) +Le compte de Bob sera débité de **-1,0042 ETH** (1 ETH pour Alice + 0,0042 ETH de frais de gaz) -Celui d'Alice sera crédité de **+1,0 ETH**. +Le compte d'Alice sera crédité de **+1,0 ETH** -Les frais de base seront brûlés **-0.00399 ETH** +Les frais de base seront brûlés **-0,00399 ETH** -Le validateur conserve le pourboire de **+0,000210 ETH** +Le validateur conserve le pourboire **+0,000210 ETH** - -![Diagramme montrant comment le gaz non utilisé est remboursé](./gas-tx.png) _Schéma adapté à partir du document [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagramme montrant comment le gaz non utilisé est remboursé](./gas-tx.png) +_Diagramme adapté de [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Tout gaz non utilisé dans une transaction est remboursé sur le compte de l'utilisateur. -### Interactions avec des contracts intelligents {#smart-contract-interactions} +### Interactions avec les contrats intelligents {#smart-contract-interactions} Du gaz est nécessaire pour toute transaction qui implique un contrat intelligent. -Les contrats intelligents peuvent également contenir des fonctions connues sous le nom de fonctions [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions) ou [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions), qui n'altèrent pas l'état du contrat. Ainsi, appeler ces fonctions à partir d'un EOA ne nécessitera aucun gaz. L'appel RPC sous-jacent pour ce scénario est [`eth_call`](/developers/docs/apis/json-rpc#eth_call). +Les contrats intelligents peuvent également contenir des fonctions connues sous le nom de fonctions `view` (https://docs.soliditylang.org/en/latest/contracts.html#view-functions) ou `pure` (https://docs.soliditylang.org/en/latest/contracts.html#pure-functions), qui ne modifient pas l'état du contrat. Ainsi, appeler ces fonctions à partir d'un EOA ne nécessitera aucun gaz. L'appel RPC sous-jacent pour ce scénario est [`eth_call`](/developers/docs/apis/json-rpc#eth_call). -Contrairement à l'utilisation de `eth_call`, ces fonctions `view` ou `pure` sont également fréquemment appelées en interne (c'est-à-dire à partir du contrat lui-même ou d'un autre contrat), ce qui entraîne un coût en gaz. +Contrairement à l'accès via `eth_call`, ces fonctions `view` ou `pure` sont également couramment appelées en interne (c'est-à-dire depuis le contrat lui-même ou depuis un autre contrat), ce qui coûte du gaz. ## Cycle de vie des transactions {#transaction-lifecycle} Voici ce qui se passe une fois qu'une transaction a été soumise : -1. Le hash de la transaction vient d'être généré grâce aux fonctions de hachage (suite d'opérations mathématiques et cryptographiques) : `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` +1. Un hachage de transaction est généré par cryptographie : + `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` 2. La transaction est ensuite diffusée sur le réseau et ajoutée à un pool de transactions composé de toutes les autres transactions réseau en attente. 3. Un validateur doit sélectionner votre transaction et l'inclure dans un bloc pour la vérifier et la considérer comme « réussie ». -4. Au fur et à mesure que le temps passe, le bloc contenant votre transaction sera mis à niveau vers « justifié » puis « finalisé ». Grâce à ces mises à niveau, vous êtes davantage assuré que votre transaction a été réussie et qu'elle ne sera jamais altérée. Une fois qu'un bloc est "finalisé", il ne peut plus être modifié par une attaque au niveau du réseau qui coûterait plusieurs milliards de dollars. +4. Au fur et à mesure que le temps passe, le bloc contenant votre transaction sera mis à niveau vers « justifié » puis « + finalisé ». Ces mises à niveau garantissent davantage + que votre transaction a été réussie et ne sera jamais modifiée. Une fois qu'un bloc est « finalisé », il ne peut être modifié que + par une attaque au niveau du réseau qui coûterait plusieurs milliards de dollars. -## Démonstration visuelle {#a-visual-demo} +## Une démo visuelle {#a-visual-demo} Regardez Austin vous guider à travers les transactions, le gaz et le minage. -## Enveloppe de transaction saisie {#typed-transaction-envelope} +## Enveloppe de transaction typée {#typed-transaction-envelope} -Ethereum avait à l'origine un unique format pour les transactions. Chaque transaction contenait une nonce, le prix du gaz, la limite de gaz, l'adresse de destination, la valeur, les données, v, r et s. Il s'agit de champs d'application [d'un RLP](/developers/docs/data-structures-and-encoding/rlp/), qui pourraient ressembler à ceci : +Ethereum avait à l'origine un unique format pour les transactions. Chaque transaction contenait une nonce, le prix du gaz, la limite de gaz, l'adresse de destination, la valeur, les données, v, r et s. Ces champs sont [codés en RLP](/developers/docs/data-structures-and-encoding/rlp/), pour ressembler à quelque chose comme ceci : `RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` -Ethereum a évolué pour prendre en charge plusieurs types de transactions afin de permettre l'implémentation de nouvelles fonctionnalités telles que les listes d'accès [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) sans affecter les formats de transactions existants. +Ethereum a évolué pour prendre en charge plusieurs types de transactions afin de permettre l'implémentation de nouvelles fonctionnalités telles que les listes d'accès et l'[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) sans affecter les anciens formats de transaction. -[EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) spécifie la façon dont cela est géré. Les transactions sont interprétées comme : +C'est l'[EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) qui permet ce comportement. Les transactions sont interprétées comme : `TransactionType || TransactionPayload` Où les champs sont définis comme : -- `TransactionType` : un nombre compris entre 0 et 0x7f, pour un total de 128 types de transactions possibles. -- `TransactionPayload` : une table arbitraire d'octets définie par le type de transaction. +- `TransactionType` - un nombre entre 0 et 0x7f, pour un total de 128 types de transactions possibles. +- `TransactionPayload` - un tableau d'octets arbitraire défini par le type de transaction. -En fonction de la valeur `TransactionType`, une transaction peut être classée comme : +En fonction de la valeur de `TransactionType`, une transaction peut être classée comme suit : -1. **Transactions de type 0 (Legacy) :** Le format de transaction original utilisé depuis le lancement d'Ethereum. Ils n'incluent pas les fonctionnalités de l'[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), telles que les calculs dynamiques des frais de gaz ou les listes d'accès pour les contrats intelligents. Les transactions originelles n'ont pas de préfixe spécifique indiquant leur type dans leur forme sérialisée, et commencent par l'octet `0xf8` lorsqu'elles utilisent le codage [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp). La valeur TransactionType pour ces transactions est `0x0`. +1. **Transactions de type 0 (héritées) :** le format de transaction original utilisé depuis le lancement d'Ethereum. Elles n'incluent pas de fonctionnalités de l'[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) telles que les calculs dynamiques des frais de gaz ou les listes d'accès pour les contrats intelligents. Les transactions héritées n'ont pas de préfixe spécifique indiquant leur type sous leur forme sérialisée. Elles commencent par l'octet `0xf8` lors de l'utilisation du codage par [préfixe de longueur récursive (RLP)](/developers/docs/data-structures-and-encoding/rlp). La valeur de TransactionType pour ces transactions est `0x0`. -2. **Transactions de type 1 :** Introduites dans [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) dans le cadre de la [mise à jour Berlin](/ethereum-forks/#berlin) d'Ethereum, ces transactions incluent un paramètre `accessList`. Cette liste spécifie les adresses et les clés de stockage auxquelles la transaction s'attend à accéder, contribuant ainsi potentiellement à réduire les coûts de [gaz](/developers/docs/gas/) pour les transactions complexes impliquant des contrats intelligents. Les variations du marché des frais EIP-1559 ne sont pas incluses dans les transactions de type 1. Les transactions de type 1 incluent également un paramètre `yParity`, qui peut être soit `0x0`, soit `0x1`, indiquant la parité de la valeur y de la signature secp256k1. Elles sont identifiées en commençant par l'octet `0x01`, et leur valeur TransactionType est `0x1`. +2. **Transactions de type 1 :** Introduites dans l'[EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) dans le cadre de la [mise à niveau Berlin](/ethereum-forks/#berlin) d'Ethereum, ces transactions incluent un paramètre `accessList`. Cette liste spécifie les adresses et les clés de stockage auxquelles la transaction s'attend à accéder, ce qui peut potentiellement aider à réduire les coûts de [gaz](/developers/docs/gas/) pour les transactions complexes impliquant des contrats intelligents. Les variations du marché des frais EIP-1559 ne sont pas incluses dans les transactions de type 1. Les transactions de type 1 incluent également un paramètre `yParity`, qui peut être `0x0` ou `0x1`, indiquant la parité de la valeur y de la signature secp256k1. Elles sont identifiées en commençant par l'octet `0x01`, et leur valeur de TransactionType est `0x1`. -3. **Transactions de type 2**, communément appelées transactions EIP-1559, sont des transactions introduites dans [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), lors de la [mise à jour London](/ethereum-forks/#london) d'Ethereum. Elles sont devenues le type de transaction standard sur le réseau Ethereum. Ces transactions introduisent un nouveau mécanisme de marché des frais qui améliore la prévisibilité en séparant les frais de transaction en frais de base et en frais prioritaires. Elles commencent par l'octet `0x02` et incluent des champs tels que `maxPriorityFeePerGas` et `maxFeePerGas`. Les transactions de Type 2 sont désormais la norme en raison de leur flexibilité et de leur efficacité. Elles sont particulièrement appréciées pendant les périodes de forte congestion du réseau car elles permettent aux utilisateurs de gérer les frais de transaction de manière plus prévisible. La valeur TransactionType pour ces transactions est `0x02`. +3. Les **transactions de type 2**, communément appelées transactions EIP-1559, sont des transactions introduites dans l'[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), lors de la [mise à niveau London](/ethereum-forks/#london) d'Ethereum. Elles sont devenues le type de transaction standard sur le réseau Ethereum. Ces transactions introduisent un nouveau mécanisme de marché des frais qui améliore la prévisibilité en séparant les frais de transaction en frais de base et en frais prioritaires. Elles commencent par l'octet `0x02` et incluent des champs tels que `maxPriorityFeePerGas` et `maxFeePerGas`. Les transactions de Type 2 sont désormais la norme en raison de leur flexibilité et de leur efficacité. Elles sont particulièrement appréciées pendant les périodes de forte congestion du réseau car elles permettent aux utilisateurs de gérer les frais de transaction de manière plus prévisible. La valeur de TransactionType pour ces transactions est `0x2`. +4. Les **transactions de type 3 (Blob)** ont été introduites dans l'[EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) dans le cadre de la [mise à niveau Dencun](/ethereum-forks/#dencun) d'Ethereum. Ces transactions sont conçues pour gérer les données de type « blob » (Binary Large Objects) de manière plus efficace, ce qui profite particulièrement aux rollups de couche 2 en offrant un moyen de publier des données sur le réseau Ethereum à moindre coût. Les transactions Blob incluent des champs supplémentaires tels que `blobVersionedHashes`, `maxFeePerBlobGas`, et `blobGasPrice`. Elles commencent par l'octet `0x03`, et leur valeur de TransactionType est `0x3`. Les transactions « blob » constituent une amélioration significative de la disponibilité des données et des capacités d'évolutivité d’Ethereum. +5. **Les transactions de type 4** ont été introduites dans l'[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) dans le cadre de la [mise à niveau Pectra](/roadmap/pectra/) d'Ethereum. Ces transactions sont conçues pour être compatibles avec l'abstraction de compte. Elles permettent aux EOA de se comporter temporairement comme des comptes de contrat intelligent sans compromettre leur fonctionnalité d'origine. Elles incluent un paramètre `authorization_list`, qui spécifie le contrat intelligent auquel l'EOA délègue son autorité. Après la transaction, le champ de code de l'EOA aura l'adresse du contrat intelligent délégué. -## Complément d'information {#further-reading} +## En savoir plus {#further-reading} -- [EIP-2718 : Enveloppe de Transaction Saisie](https://eips.ethereum.org/EIPS/eip-2718) +- [EIP-2718 : Enveloppe de transaction typée](https://eips.ethereum.org/EIPS/eip-2718) _Une ressource communautaire vous a aidé ? Modifiez cette page et ajoutez-la !_ ## Sujets connexes {#related-topics} - [Comptes](/developers/docs/accounts/) -- [Machine Virtuelle d'Ethereum (EVM)](/developers/docs/evm/) +- [Machine virtuelle Ethereum (EVM)](/developers/docs/evm/) - [Gaz](/developers/docs/gas/) diff --git a/public/content/translations/fr/developers/docs/web2-vs-web3/index.md b/public/content/translations/fr/developers/docs/web2-vs-web3/index.md index a584528ae96..b495276a151 100644 --- a/public/content/translations/fr/developers/docs/web2-vs-web3/index.md +++ b/public/content/translations/fr/developers/docs/web2-vs-web3/index.md @@ -1,10 +1,10 @@ --- -title: Web2 et Web3 -description: +title: Web2 vs Web3 +description: "Comparez les services Web2 centralisés avec les applications Web3 décentralisées basées sur la technologie blockchain Ethereum." lang: fr --- -Web2 fait référence à la version d'Internet que la plupart d'entre nous connaissent aujourd'hui. Un Internet dominé par les sociétés qui fournissent des services en échange de vos données personnelles. Dans le contexte d'Ethereum, Web3 fait référence aux applications décentralisées qui s'exécutent sur la blockchain. Ce sont des applications qui permettent à quiconque de participer sans monétiser ses données personnelles. +Web2 fait référence à la version d'Internet que la plupart d'entre nous connaissent aujourd'hui. Un Internet dominé par les sociétés qui fournissent des services en échange de vos données personnelles. Dans le contexte Ethereum, le Web3 fait référence aux applications décentralisées qui s'exécutent sur la blockchain. Ce sont des applications qui permettent à quiconque de participer sans monétiser ses données personnelles. Vous recherchez une ressource plus conviviale pour les débutants ? Consultez notre [introduction au Web3](/web3/). @@ -19,15 +19,15 @@ De nombreux développeurs Web3 ont choisi de construire des dApps en raison de l ## Comparaisons pratiques {#practical-comparisons} -| Web2 | Web3 | -| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -| Twitter peut censurer n'importe quel compte ou tweet. | Les tweets Web3 ne pourraient pas être censurés, car le contrôle est décentralisé. | -| Un service de paiement peut décider de ne pas autoriser les paiements pour certains types de travaux. | Les applications de paiement Web3 ne requièrent aucune donnée personnelle et ne peuvent pas empêcher les paiements. | +| Web2 | Web3 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Twitter peut censurer n'importe quel compte ou tweet. | Les tweets Web3 ne pourraient pas être censurés, car le contrôle est décentralisé. | +| Un service de paiement peut décider de ne pas autoriser les paiements pour certains types de travaux. | Les applications de paiement Web3 ne requièrent aucune donnée personnelle et ne peuvent pas empêcher les paiements. | | Les serveurs des applications de travail à la tâche (ou gig-économie) pourraient fermer et affecter les revenus des travailleurs. | Les serveurs Web3 ne peuvent pas fermer. Ils utilisent Ethereum, un réseau décentralisé de milliers d'ordinateurs, comme backend. | Cela ne signifie pas pour autant que tous les services doivent être transformés en dApps. Ces exemples illustrent simplement les principales différences entre les services Web2 et Web3. -## Limitation du Web3 {#web3-limitations} +## Limites du Web3 {#web3-limitations} Le Web3 affiche actuellement quelques limitations : @@ -36,27 +36,27 @@ Le Web3 affiche actuellement quelques limitations : - Accessibilité – le manque d’intégration dans les navigateurs Web modernes rend le web3 moins accessible à la plupart des utilisateurs. - Coût : La plupart des dApps réussies publient de très petites portions de leur code sur la blockchain car c'est coûteux. -## Centralisation et décentralisation {#centralization-vs-decentralization} +## Centralisation ou décentralisation {#centralization-vs-decentralization} Dans le tableau ci-dessous, nous répertorions certains des avantages et inconvénients des réseaux numériques centralisés et décentralisés. -| Systèmes centralisés | Systèmes décentralisés | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Faible diamètre de réseau (tous les participants sont connectés à une autorité centrale). L'information se propage rapidement, car la propagation est gérée par une autorité centrale avec de nombreuses ressources informatiques. | Les participants les plus éloignés du réseau peuvent potentiellement être très éloignés les uns des autres. Les informations diffusées d'un côté du réseau peuvent prendre beaucoup de temps pour atteindre l'autre côté. | -| Généralement plus performants (débit plus élevé, moins de ressources informatiques totales dépensées) et plus faciles à implémenter. | Habituellement moins performants (débit plus faible, plus de ressources informatiques totales dépensées) et plus complexes à implémenter. | -| En cas de conflits de données, la résolution est claire et facile : la source de vérité est l'autorité centrale. | Un protocole (souvent complexe) est nécessaire pour le règlement des litiges. si les pairs font des déclarations contradictoires sur l'état des données sur lesquelles les participants sont censés être synchronisés. | -| Point de défaillance unique : les acteurs malveillants peuvent démanteler le réseau en ciblant l'autorité centrale. | Aucun point de défaillance unique : le réseau peut toujours fonctionner même si une grande partie des participants sont attaqués/retirés. | +| Systèmes centralisés | Systèmes décentralisés | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Faible diamètre de réseau (tous les participants sont connectés à une autorité centrale). L'information se propage rapidement, car la propagation est gérée par une autorité centrale avec de nombreuses ressources informatiques. | Les participants les plus éloignés du réseau peuvent potentiellement être très éloignés les uns des autres. Les informations diffusées d'un côté du réseau peuvent prendre beaucoup de temps pour atteindre l'autre côté. | +| Généralement plus performants (débit plus élevé, moins de ressources informatiques totales dépensées) et plus faciles à implémenter. | Habituellement moins performants (débit plus faible, plus de ressources informatiques totales dépensées) et plus complexes à implémenter. | +| En cas de conflits de données, la résolution est claire et facile : la source de vérité est l'autorité centrale. | Un protocole (souvent complexe) est nécessaire pour le règlement des litiges. si les pairs font des déclarations contradictoires sur l'état des données sur lesquelles les participants sont censés être synchronisés. | +| Point de défaillance unique : les acteurs malveillants peuvent démanteler le réseau en ciblant l'autorité centrale. | Aucun point de défaillance unique : le réseau peut toujours fonctionner même si une grande partie des participants sont attaqués/retirés. | | La coordination entre les participants au réseau est beaucoup plus facile et est gérée par une autorité centrale. L'autorité centrale peut contraindre les participants au réseau à adopter des mises à niveau, des mises à niveau de protocole, etc. avec très peu de friction. | La coordination est souvent difficile, car aucun agent n'a le dernier mot sur les décisions prises au niveau du réseau, les mises à niveau des protocoles, etc. Dans le pire des cas, le réseau est enclin à se fracturer lorsqu'il y a des désaccords sur les changements de protocole. | -| L'autorité centrale peut censurer les données, en empêchant potentiellement certaines parties du réseau d'interagir avec le reste du réseau. | La censure est beaucoup plus difficile, car les informations ont de nombreux moyens de se propager sur le réseau. | -| La participation au réseau est contrôlée par l'autorité centrale. | Tout le monde peut participer au réseau. Il n’y a pas de "gardiens". Idéalement, les coûts de participation sont très faibles. | +| L'autorité centrale peut censurer les données, en empêchant potentiellement certaines parties du réseau d'interagir avec le reste du réseau. | La censure est beaucoup plus difficile, car les informations ont de nombreux moyens de se propager sur le réseau. | +| La participation au réseau est contrôlée par l'autorité centrale. | Tout le monde peut participer au réseau. Il n’y a pas de "gardiens". Idéalement, les coûts de participation sont très faibles. | Notez qu'il s'agit là de modèles généraux qui ne sont pas nécessairement valables pour tous les réseaux. En outre, en réalité, le degré de centralisation ou de décentralisation d'un réseau repose sur un spectre. Aucun réseau n'est entièrement centralisé ni entièrement décentralisé. -## Complément d'information {#further-reading} +## En savoir plus {#further-reading} -- [Qu'est-ce que le Web3 ?](/web3/) - _ethereum.org_ -- [L'Architecture d'une application Web 3.0](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) - _Preethi Kasireddy_ -- [The Meaning of Decentralization](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _- Vitalik Buterin, 6 février 2017 _ -- [Pourquoi la décentralisation est importante](https://medium.com/s/story/why-decentralization-matters-5e3f79f7638e) _- Chris Dixon, 18 février 2018_ -- [Qu'est-ce que le Web 3.0 et pourquoi c'est important](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _ - Max Mersch et Richard Muirhead, 31 décembre 2019_ -- [Pourquoi nous avons besoin du Web 3.0](https://medium.com/@gavofyork/why-we-need-web-3-0-5da4f2bf95ab) _12 Sep 2018 - Gavin Wood_ +- [Qu'est-ce que le Web3 ?](/web3/) - _ethereum.org_ +- [L'architecture d'une application Web 3.0](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) - _Preethi Kasireddy_ +- [La signification de la décentralisation](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _6 février 2017 - Vitalik Buterin_ +- [Pourquoi la décentralisation est importante](https://onezero.medium.com/why-decentralization-matters-5e3f79f7638e) _18 février 2018 - Chris Dixon_ +- [Qu'est-ce que le Web 3.0 et pourquoi est-ce important](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _31 décembre 2019 - Max Mersch et Richard Muirhead_ +- [Pourquoi nous avons besoin du Web 3.0](https://gavofyork.medium.com/why-we-need-web-3-0-5da4f2bf95ab) _12 septembre 2018 - Gavin Wood_ diff --git a/public/content/translations/fr/developers/docs/wrapped-eth/index.md b/public/content/translations/fr/developers/docs/wrapped-eth/index.md index ae9c00f3a4d..0ebe1ff42df 100644 --- a/public/content/translations/fr/developers/docs/wrapped-eth/index.md +++ b/public/content/translations/fr/developers/docs/wrapped-eth/index.md @@ -1,6 +1,6 @@ --- title: Qu'est-ce que l'Ether symbolique (WETH) -description: Une introduction à l'Ether symbolique (WETH) — un système compatible ERC20 pour l'ether (ETH). +description: "Une introduction à l'Ether symbolique (WETH) — un système compatible ERC20 pour l'ether (ETH)." lang: fr --- @@ -35,19 +35,16 @@ Vous pouvez échanger le WETH contre de l'ETH en utilisant le contrat intelligen Vous payez des frais de gas pour encapsuler ou désencapsuler des ETH en utilisant le smart contract WETH. - Le WETH est généralement considéré comme sûr car il est basé sur un contrat intelligent simple et éprouvé. Le contrat intelligent WETH a également été formellement vérifié, ce qui constitue la norme de sécurité la plus élevée pour les contrats intelligents sur Ethereum. - Outre la [version canonique de WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) décrite sur cette page, il existe d'autres variantes en circulation. Il peut s'agir de tokens personnalisés créés par des développeurs d'applications ou de versions émises sur d'autres blockchains, qui peuvent se comporter différemment ou avoir des propriétés de sécurité différentes. **Vérifiez toujours les informations sur le jeton pour savoir avec quelle implémentation de WETH vous interagissez.** - @@ -55,7 +52,6 @@ Outre la [version canonique de WETH](https://etherscan.io/token/0xc02aaa39b223fe - [Réseau principal d'Ethereum](https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) - [Arbitrum](https://arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1) - [Optimism](https://optimistic.etherscan.io/token/0x4200000000000000000000000000000000000006) - ## En savoir plus {#further-reading} diff --git a/public/content/translations/fr/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md b/public/content/translations/fr/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md index e8ae60a4b66..38330d7b521 100644 --- a/public/content/translations/fr/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md +++ b/public/content/translations/fr/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md @@ -1,33 +1,32 @@ --- -title: Introduction à Ethereum pour développeurs Python, partie 1 -description: Une introduction au développement Ethereum, particulièrement utile aux personnes disposant de connaissances en langage de programmation Python +title: "Introduction à Ethereum pour les développeurs Python, partie 1" +description: "Une introduction au développement Ethereum, particulièrement utile pour ceux qui connaissent le langage de programmation Python." author: Marc Garreau lang: fr -tags: - - "python" - - "web3.py" +tags: [ "python", "web3.py" ] skill: beginner published: 2020-09-08 source: Snake charmers sourceUrl: https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt-1/ --- -Vous avez donc entendu parler d'Ethereum et êtes prêts à passer de l'autre côté du miroir ? Cet article couvrira rapidement certaines fonctionnalités de base propres aux blockchains, puis vous permettra d'interagir avec une simulation de nœud Ethereum - lecture des données de blocs, vérification des soldes de comptes et envoi de transactions. En cours de route, nous soulignerons les différences entre les méthodes classiques de création d'application et ce nouveau paradigme décentralisé. +Alors, vous avez entendu parler de ce truc qu'est Ethereum et vous êtes prêt à vous aventurer dans le terrier du lapin ? Cet article couvrira rapidement quelques notions de base de la blockchain, puis vous fera interagir avec un nœud Ethereum simulé – lecture de données de blocs, vérification des soldes de compte et envoi de transactions. Chemin faisant, nous soulignerons les différences entre les manières traditionnelles de créer des applications et ce nouveau paradigme décentralisé. -## Prérequis (simple) {#soft-prerequisites} +## Prérequis (non stricts) {#soft-prerequisites} -Ce post se veut accessible à une large catégorie de développeurs. L'emploi d'[outils Python](/developers/docs/programming-languages/python/) sera réalisé, mais ils ne serviront qu'à véhiculer les idées – Ne vous inquiétez pas si vous n'êtes pas développeur Python. Toutefois, je vais faire quelques hypothèses sur ce que vous savez déjà, afin que nous puissions rapidement passer aux sujets spécifiques à Ethereum. +Cet article se veut accessible à un large éventail de développeurs. Nous utiliserons des [outils Python](/developers/docs/programming-languages/python/), mais ils ne sont qu'un prétexte pour présenter les idées. Pas de problème si vous n'êtes pas un développeur Python. Je vais toutefois partir de quelques hypothèses sur ce que vous savez déjà, afin que nous puissions rapidement passer aux éléments spécifiques à Ethereum. -Hypothèses: +Hypothèses : -- Vous êtes en mesure d'utiliser un terminal, -- Vous avez déjà écrit quelques lignes de code Python, -- La version 3.6 ou supérieure de Python est installée sur votre machine (l'utilisation d'un [environnement virtuel](https://realpython.com/effective-python-environment/#virtual-environments) est fortement recommandée), et -- vous avez déjà utilisé `pip`, l'installateur de paquets de Python. Encore une fois, si l'un de ces éléments n'est pas vrai, ou si vous ne prévoyez pas de reproduire le code de cet article, vous resterez capable de comprendre son contenu sans grande difficulté. +- Vous savez utiliser un terminal, +- Vous avez écrit quelques lignes de code Python, +- Python version 3.6 ou supérieure est installé sur votre machine (l'utilisation d'un [environnement virtuel](https://realpython.com/effective-python-environment/#virtual-environments) est fortement encouragée), et +- vous avez utilisé `pip`, le programme d'installation de paquets de Python. + Encore une fois, si l'un de ces points ne s'applique pas à vous, ou si vous ne prévoyez pas de reproduire le code de cet article, vous devriez tout de même pouvoir suivre sans problème. -## Blockchains, en bref {#blockchains-briefly} +## Les blockchains, en bref {#blockchains-briefly} -Il y a de nombreuses façons de décrire Ethereum, mais son cœur repose sur la Blockchain. Les Blockchains sont constituées d'une série de blocs, alors commençons par là. En termes simples, chaque bloc de la blockchain Ethereum n'est qu'un ensemble de métadonnées et de transactions. Dans le format JSON, cela ressemble à ceci : +Il y a de nombreuses façons de décrire Ethereum, mais au cœur de celui-ci se trouve une blockchain. Les blockchains sont constituées d'une série de blocs, alors commençons par là. En termes simples, chaque bloc de la blockchain Ethereum n'est qu'un ensemble de métadonnées et une liste de transactions. Au format JSON, cela ressemble à ceci : ```json { @@ -39,108 +38,108 @@ Il y a de nombreuses façons de décrire Ethereum, mais son cœur repose sur la } ``` -Chaque [bloc](/developers/docs/blocks/) possède une référence du bloc l'ayant précédé ; le `parentHash` est simplement le hash du bloc précédent. +Chaque [bloc](/developers/docs/blocks/) possède une référence au bloc qui le précède ; le `parentHash` est simplement le hachage du bloc précédent. -Note : le réseau Ethereum utilise régulièrement des fonctions de hachage pour produire des valeurs de taille fixe (« hashes »). Les hachages (« hashes ») jouent un rôle important dans le réseau Ethereum, vous pouvez les considérer comme des identifiants uniques pour le moment. +Remarque : Ethereum utilise régulièrement des fonctions de hachage pour produire des valeurs de taille fixe (« hachages »). Les hachages jouent un rôle important dans Ethereum, mais pour l'instant, vous pouvez simplement les considérer comme des identifiants uniques. -![Un diagramme décrivant la blockchain ainsi que les données internes de chaque bloc](./blockchain-diagram.png) +![Un diagramme représentant une blockchain, y compris les données à l'intérieur de chaque bloc](./blockchain-diagram.png) -_Une blockchain est essentiellement une liste liée ; chaque bloc a une référence au bloc précédent._ +_Une blockchain est essentiellement une liste chaînée ; chaque bloc a une référence au bloc précédent._ -Cette structure de données n'est pas nouvelle, mais les règles (c'est-à-dire les protocoles « peer-to-peer ») qui régissent le réseau le sont. Il n’y a pas d’autorité centrale; le réseau d'utilisateurs (pairs) doit collaborer pour pérenniser le réseau et s'affronter pour décider quelles transactions inclure dans le bloc suivant. Donc, quand vous voulez envoyer de l'argent à un ami, vous devrez diffuser cette transaction sur le réseau, puis attendre qu'elle soit incluse dans un bloc à venir. +Cette structure de données n'a rien de nouveau, mais les règles (c'est-à-dire les protocoles pair-à-pair) qui régissent le réseau le sont. Il n'y a pas d'autorité centrale ; le réseau de pairs doit collaborer pour maintenir le réseau, et rivaliser pour décider quelles transactions inclure dans le bloc suivant. Ainsi, lorsque vous voulez envoyer de l'argent à un ami, vous devez diffuser cette transaction sur le réseau, puis attendre qu'elle soit incluse dans un prochain bloc. -La seule façon pour la chaîne de blocs de vérifier que l'argent a vraiment été envoyé d'un utilisateur à un autre est d'utiliser une devise native à (à savoir créée et régie par) la blockchain. Sur le réseau Ethereum, cette devise est appelée « ether », et la blockchain Ethereum contient le seul enregistrement officiel des soldes de compte. +La seule façon pour la blockchain de vérifier que de l'argent a bien été envoyé d'un utilisateur à un autre est d'utiliser une devise native (c'est-à-dire, créée et régie par) de cette blockchain. Dans Ethereum, cette devise s'appelle l'ether, et la blockchain Ethereum contient le seul enregistrement officiel des soldes des comptes. ## Un nouveau paradigme {#a-new-paradigm} -Cette nouvelle pile de technologies décentralisées a créé de nouveaux outils de développement. De tels outils existent dans de nombreux langages de programmation, mais nous allons les explorer via le language Python. Encore une fois : même si Python n’est pas votre langage de choix, il ne devrait pas être difficile de le comprendre. +Cette nouvelle pile technologique décentralisée a donné naissance à de nouveaux outils pour les développeurs. De tels outils existent dans de nombreux langages de programmation, mais nous allons les explorer à travers le prisme de Python. Pour le répéter : même si Python n'est pas votre langage de prédilection, vous ne devriez pas avoir de mal à suivre. -Les développeurs Python qui veulent interagir avec le réseau Ethereum sont encouragés à utiliser [Web3.py](https://web3py.readthedocs.io/). Web3.py est une bibliothèque qui simplifie grandement la façon dont vous vous connectez à un nœud Ethereum, et par la suite envoyer et recevoir des données. +Les développeurs Python qui souhaitent interagir avec Ethereum se tourneront probablement vers [Web3.py](https://web3py.readthedocs.io/). Web3.py est une bibliothèque qui simplifie grandement la connexion à un nœud Ethereum, ainsi que l'envoi et la réception de données depuis celui-ci. -Note : Les notions de « noeud Ethereum » et de « client Ethereum » sont utilisées de façon interchangeable. Dans les deux cas, il se réfère au logiciel qu'un participant au réseau Ethereum exécute. Ce logiciel peut lire les données de bloc, recevoir des mises à jour lorsque de nouveaux blocs sont ajoutés à la chaîne, diffuser de nouvelles transactions, et encore bien davantage. Techniquement, le client est le logiciel , le nœud est l'ordinateur qui exécute le logiciel. +Remarque : Les termes « nœud Ethereum » et « client Ethereum » sont utilisés de manière interchangeable. Dans les deux cas, cela fait référence au logiciel qu'un participant du réseau Ethereum exécute. Ce logiciel peut lire les données des blocs, recevoir des mises à jour lorsque de nouveaux blocs sont ajoutés à la chaîne, diffuser de nouvelles transactions, et plus encore. Techniquement, le client est le logiciel, le nœud est l'ordinateur qui exécute le logiciel. -[Les clients Ethereum](/developers/docs/nodes-and-clients/) peuvent être configurés pour être accessibles par [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP ou Websockets, donc Web3. devra respecter cette configuration. Web3.py fait référence à ces options de connexion en tant que **fournisseurs**. Il vous faudra choisir l'un des trois fournisseurs pour lier l'instance Web3.py à votre nœud. +Les [clients Ethereum](/developers/docs/nodes-and-clients/) peuvent être configurés pour être joignables par [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP ou Websockets, donc Web3.py devra refléter cette configuration. Web3.py désigne ces options de connexion sous le nom de **fournisseurs**. Il vous faudra choisir l'un des trois fournisseurs pour lier l'instance Web3.py à votre nœud. -![Un diagramme montrant comment web3.py utilise IPC pour connecter votre application à un nœud Ethereum](./web3py-and-nodes.png) +![Un diagramme montrant comment web3.py utilise l'IPC pour connecter votre application à un nœud Ethereum](./web3py-and-nodes.png) -_Configurez le noeud Ethereum et Web3.py afin qu'ils communiquent via le même protocole, par exemple via IPC dans ce diagramme._ +_Configurez le nœud Ethereum et Web3.py pour qu'ils communiquent via le même protocole, par exemple, l'IPC dans ce diagramme._ -Une fois que Web3.py est correctement configuré, vous pouvez commencer à interagir avec la blockchain. Voici quelques exemples d'utilisation de Web3.py pour avoir un aperçu de ce qui va se passer : +Une fois que Web3.py est correctement configuré, vous pouvez commencer à interagir avec la blockchain. Voici quelques exemples d'utilisation de Web3.py en guise d'aperçu de ce qui suit : ```python -# read block data: +# lire les données du bloc : w3.eth.get_block('latest') -# send a transaction: +# envoyer une transaction : w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) ``` ## Installation {#installation} -Dans ce qui suit, nous allons juste travailler au sein de l'interpréteur Python. Nous n'allons pas créer de répertoires, fichiers, classes ou fonctions. +Dans ce tutoriel, nous travaillerons uniquement dans un interpréteur Python. Nous ne créerons ni répertoires, ni fichiers, ni classes ou fonctions. -Note : dans les exemples ci-dessous, les commandes qui commencent par `$` sont censées être exécutées dans le terminal. (Ne tapez pas le `$`, cela signifie simplement que c'est le début de la ligne.) +Remarque : dans les exemples ci-dessous, les commandes qui commencent par `$` sont destinées à être exécutées dans le terminal. (Ne tapez pas le `$`, il indique simplement le début de la ligne.) -Tout d'abord, installez [IPython](https://ipython.org/) pour avoir un environnement convivial à explorer. IPython propose entre autres une fonctionnalité d'auto-completion en appuyant sur la touche TAB, ce qui facilite la navigation dans Web3.py. +Tout d'abord, installez [IPython](https://ipython.org/) pour disposer d'un environnement convivial pour l'exploration. IPython offre, entre autres fonctionnalités, la complétion par tabulation, ce qui facilite grandement la découverte des possibilités de Web3.py. ```bash pip install ipython ``` -Web3.py est publié sous le nom `web3`. Installez-le comme suit : +Web3.py est publié sous le nom `web3`. Installez-le comme suit : ```bash pip install web3 ``` -Encore une chose : nous allons simuler une blockchain plus tard, ce qui requiert quelques dépendances supplémentaires. Vous pouvez les installer via : +Encore une chose : nous allons simuler une blockchain plus tard, ce qui requiert quelques dépendances supplémentaires. Vous pouvez les installer via : ```bash pip install 'web3[tester]' ``` -C'est tout ! +Vous êtes fin prêt ! -Note : Le package `web3[tester]` marche jusqu'a Python 3.10.xx +Remarque : Le package `web3[tester]` fonctionne jusqu'à Python 3.10.xx -## Lancer un sandbox {#spin-up-a-sandbox} +## Lancer un bac à sable {#spin-up-a-sandbox} -Ouvrez un nouvel environnement Python en exécutant `ipython` dans votre terminal. Ceci est comparable à l'exécution de `python`=, mais apporte plus d'avantages. +Ouvrez un nouvel environnement Python en exécutant `ipython` dans votre terminal. C'est comparable à l'exécution de `python`, mais avec beaucoup plus de fonctionnalités. ```bash ipython ``` -Cela affichera quelques informations sur les versions de Python et de IPython que vous utilisez, puis vous devriez voir une invite de saisie : +Cela affichera quelques informations sur les versions de Python et de IPython que vous utilisez, puis vous devriez voir une invite de saisie : ```python In [1]: ``` -Vous regardez un shell Python interactif. Essentiellement, il s'agit d'un bac à sable pour jouer. Si vous êtes arrivés jusqu’ici, il est temps d’importer Web3.py : +Vous avez maintenant devant vous un shell Python interactif. Essentiellement, c'est un bac à sable dans lequel vous pouvez expérimenter. Si vous êtes arrivé jusqu’ici, il est temps d’importer Web3.py : ```python In [1]: from web3 import Web3 ``` -## Introduction du module Web3 {#introducing-the-web3-module} +## Présentation du module Web3 {#introducing-the-web3-module} -En plus d'être une passerelle vers Ethereum, le module [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api) offre quelques fonctions pratiques. Examinons-en quelques-unes. +En plus d'être une passerelle vers Ethereum, le module [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api) offre quelques fonctions utilitaires. Explorons-en quelques-unes. -Dans une application Ethereum, vous devrez généralement convertir des libellés de devises. Le module Web3 fournit quelques méthodes juste pour cela : [fromWei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) et [toWei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei). +Dans une application Ethereum, vous aurez souvent besoin de convertir des dénominations de monnaie. Le module Web3 fournit quelques méthodes d'assistance à cet effet : [from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) et [to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei). -Remarque : les ordinateurs sont notoirement peu efficaces pour la gestion des nombres décimaux. Pour contourner cela, les développeurs stockent souvent les montants en dollar en centimes. Par exemple, un article avec un prix de 5,99 $ peut être stocké dans la base de données comme 599. +Note: Computers are notoriously bad at handling decimal math. To get around this, developers often store dollar amounts in cents. For example, an item with a price of $5.99 may be stored in the database as 599. -Un schéma similaire est utilisé lors de la gestion des transactions en ether. Cependant, au lieu de deux décimaux, l'ether en a 18 ! La plus petite dénomination d'ether s'appelle wei, c'est la valeur spécifiée lors de l'envoi des transactions. +Un modèle similaire est utilisé lors du traitement des transactions en ether. Cependant, au lieu de deux décimales, l'ether en a 18 ! La plus petite dénomination d'ether s'appelle wei, c'est donc la valeur spécifiée lors de l'envoi de transactions. 1 ether = 1000000000000000000 wei -1 wei = 0,00000000000001 ether +1 wei = 0,000000000000000001 ether -Essayez de convertir certaines valeurs depuis et vers le wei. Notez qu'il y a [des noms pour un grand nombre de dénominations](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations) entre ether et wei. L'une des plus connues est le **gwei**, car c'est souvent la façon dont les frais de transaction sont représentés. +Essayez de convertir certaines valeurs depuis et vers le wei. Notez qu'[il existe des noms pour la plupart des dénominations](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations) entre l'ether et le wei. L'un des plus connus est le **gwei**, car c'est souvent ainsi que les frais de transaction sont représentés. ```python In [2]: Web3.to_wei(1, 'ether') @@ -150,49 +149,50 @@ In [3]: Web3.from_wei(500000000, 'gwei') Out[3]: Decimal('0.5') ``` -Les autres méthodes utilitaires du module Web3 incluent les convertisseurs de format de données (par exemple, [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), les méthodes pour gérer les adresses (e. ., [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)), et les fonctions de hachage (par exemple, [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak)). Beaucoup d'entre elles seront étudiées plus tard dans la série. Pour afficher toutes les méthodes et propriétés disponibles, utilisez l'auto-complétion de IPython en tapant `Web3`. et en appuyant sur la touche tabulation deux fois après le point. +D'autres méthodes utilitaires du module Web3 comprennent des convertisseurs de format de données (par ex., [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), des assistants d'adresse (par ex., [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)), et des fonctions de hachage (par ex., [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak)). Beaucoup d'entre elles seront abordées plus tard dans la série. Pour afficher toutes les méthodes et propriétés disponibles, utilisez l'auto-complétion d'IPython en tapant `Web3`. et en appuyant deux fois sur la touche tabulation après le point. -## Parler à la chaîne {#talk-to-the-chain} +## Discuter avec la chaîne {#talk-to-the-chain} -Ces méthodes sont très intéressantes, mais passons à la blockchain. L'étape suivante est de configurer Web3.py à des fins de communication avec un noeud Ethereum. Ici, nous avons la possibilité d'utiliser les fournisseurs IPC, HTTP, ou Websocket. +Les méthodes de commodité sont très bien, mais passons à la blockchain. L'étape suivante consiste à configurer Web3.py pour qu'il communique avec un nœud Ethereum. Ici, nous avons la possibilité d'utiliser les fournisseurs IPC, HTTP ou Websocket. -Nous n'allons pas explorer cette voie, mais un exemple de flux de travail complet en utilisant le fournisseur HTTP pourrait ressembler à ceci : +Nous n'allons pas suivre cette voie, mais un exemple de flux de travail complet utilisant le fournisseur HTTP pourrait ressembler à ceci : -- Télécharger un nœud Ethereum, par exemple [Geth](https://geth.ethereum.org/). -- Démarrez Geth dans une seule fenêtre de terminal et attendez qu'il synchronise le réseau. Le port HTTP par défaut est `8545`, mais il est configurable. -- Dites à Web3.py de se connecter au nœud via HTTP, sur `localhost:8545`. `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- Téléchargez un nœud Ethereum, par ex., [Geth](https://geth.ethereum.org/). +- Démarrez Geth dans une fenêtre de terminal et attendez qu'il synchronise le réseau. Le port HTTP par défaut est `8545`, mais il est configurable. +- Indiquez à Web3.py de se connecter au nœud via HTTP, sur `localhost:8545`. + `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` - Utilisez l'instance `w3` pour interagir avec le nœud. -Bien qu’il s’agisse d’une « vraie » façon de le faire, le processus de synchronisation prend des heures et est inutile si vous voulez juste un environnement de développement. Web3.py expose un quatrième fournisseur à cet effet, l'**EthereumTesterProvider**. Ce fournisseur de testeur est relié à un nœud Ethereum simulé avec des autorisations réduites et des fausses devises pour jouer. +Bien que ce soit une façon « réelle » de le faire, le processus de synchronisation prend des heures et est inutile si vous voulez juste un environnement de développement. Web3.py expose un quatrième fournisseur à cet effet, l'**EthereumTesterProvider**. Ce fournisseur de test est relié à un nœud Ethereum simulé avec des autorisations assouplies et de la fausse monnaie pour s'exercer. -![Un diagramme montrant l'EthereumTesterProvider reliant votre application web3.py à un nœud Ethereum simulé](./ethereumtesterprovider.png) +![Un diagramme montrant EthereumTesterProvider reliant votre application web3.py à un nœud Ethereum simulé](./ethereumtesterprovider.png) -_L'EthereumTesterProvider se connecte à un noeud simulé et est pratique pour des environnements de développement rapides._ +_L'EthereumTesterProvider se connecte à un nœud simulé et est pratique pour des environnements de développement rapides._ -Ce nœud simulé s'appelle [eth-testeur](https://github.com/ethereum/eth-tester) et nous l'avons installé dans le cadre de la commande `pip install web3[tester]`. Configurer Web3.py pour qu'il utilise ce fournisseur de testeur est aussi simple que : +Ce nœud simulé s'appelle [eth-tester](https://github.com/ethereum/eth-tester) et nous l'avons installé dans le cadre de la commande `pip install web3[tester]`. Configurer Web3.py pour utiliser ce fournisseur de test est aussi simple que : ```python In [4]: w3 = Web3(Web3.EthereumTesterProvider()) ``` -Maintenant vous êtes prêt à surfer sur la chaîne ! Ce n'est pas une chose que les gens disent. Je viens juste de l'inventer. Faisons un tour rapide. +Vous êtes maintenant prêt à surfer sur la chaîne ! Personne ne dit ça. Je viens de l'inventer. Faisons un tour rapide. ## Le tour rapide {#the-quick-tour} -Tout d'abord, une vérification : +Tout d'abord, une vérification de base : ```python In [5]: w3.is_connected() Out[5]: True ``` -Étant donné que nous utilisons le fournisseur de testeur, ce test n'est pas très important. Toutefois, s'il échoue, il y a des chances que vous ayez mal tapé quelque chose lors de l'instanciation de la variable `w3`. Vérifiez bien que vous avez inclus les parenthèses intérieures, à savoir `Web3.EthereumTesterProvider()`. +Étant donné que nous utilisons le fournisseur de test, ce test n'est pas très utile, mais s'il échoue, il y a de fortes chances que vous ayez mal saisi quelque chose lors de l'instanciation de la variable `w3`. Vérifiez que vous avez bien inclus les parenthèses intérieures, c'est-à-dire `Web3.EthereumTesterProvider()`. -## Arrêt #1 : Les [comptes](/developers/docs/accounts/) {#tour-stop-1-accounts} +## Arrêt n°1 : [comptes](/developers/docs/accounts/) {#tour-stop-1-accounts} -Afin de faciliter les tests, le fournisseur de testeur a créé des comptes et les a préchargés avec un ether de test. +Par commodité, le fournisseur de test a créé des comptes et les a préchargés avec de l'ether de test. -D’abord, observons une liste de ces comptes : +D'abord, jetons un œil à la liste de ces comptes : ```python In [6]: w3.eth.accounts @@ -201,27 +201,27 @@ Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] ``` -Si vous exécutez cette commande, vous devriez voir une liste de dix chaînes de caractères qui commencent par `0x`. Chacune d'entre elles est une **adresse publique** qui est, à certains égards, similaire au numéro de compte sur un compte bancaire. Vous pourriez fournir cette adresse à quelqu'un qui voudrait vous envoyer des ethers. +Si vous exécutez cette commande, vous devriez voir une liste de dix chaînes de caractères qui commencent par `0x`. Chacune est une **adresse publique** et est, à certains égards, analogue au numéro de compte d'un compte courant. Vous fourniriez cette adresse à quelqu'un qui voudrait vous envoyer de l'ether. -Comme mentionné, le fournisseur de testeur a préchargé chacun de ces comptes avec des ethers de test. Cherchons maintenant combien d'ethers contient le premier compte : +Comme mentionné, le fournisseur de test a préchargé chacun de ces comptes avec de l'ether de test. Voyons combien il y en a dans le premier compte : ```python In [7]: w3.eth.get_balance(w3.eth.accounts[0]) Out[7]: 1000000000000000000000000 ``` -Beaucoup de zéros ! Avant d'aller à la fausse banque et de vous remplir les poches tout le long du trajet, rappellez-vous la leçon de tout à l'heure sur les dénominations monétaires. La valeur en ether est présentée dans sa plus petite dénomination, le wei. Convertissons-la en ether : +Ça fait beaucoup de zéros ! Avant d'aller rire jusqu'à la fausse banque, rappelez-vous la leçon précédente sur les dénominations de devises. Les valeurs en ether sont représentées dans la plus petite dénomination, le wei. Convertissons cela en ether : ```python In [8]: w3.from_wei(1000000000000000000000000, 'ether') Out[8]: Decimal('1000000') ``` -Un million d'ethers de test — ça reste toujours intéressant. +Un million d'ethers de test... ce n'est pas si mal. -## Arrêt #2 : Les données de bloc {#tour-stop-2-block-data} +## Arrêt n°2 : données des blocs {#tour-stop-2-block-data} -Jetons un coup d’œil à l’état de cette blockchain simulée : +Jetons un coup d’œil à l’état de cette blockchain simulée : ```python In [9]: w3.eth.get_block('latest') @@ -234,15 +234,15 @@ Out[9]: AttributeDict({ }) ``` -Beaucoup d'informations sont retournées à propos d'un bloc, mais juste quelques choses à signaler ici : +Beaucoup d'informations sont retournées à propos d'un bloc, mais il y a juste quelques points à souligner ici : -- Le numéro de bloc est zéro — peu importe depuis combien de temps vous avez configuré le fournisseur de testeur. Contrairement au véritable réseau Ethereum qui mine un nouveau bloc toutes les 12 secondes, cette simulation restera en attente jusqu'à ce que vous lui donniez une tâche à accomplir. -- `transactions` est une liste vide pour la même raison : nous n’avons rien fait pour le moment. Ce premier bloc est un bloc **vide**, juste conçu pour démarrer la chaîne. -- Notez que le `parentHash` n'est qu'un amas d'octets vides. Cela signifie qu'il s'agit du premier bloc de la chaîne, également connu sous le nom de **bloc de genèse**. +- Le numéro de bloc est zéro, peu importe depuis combien de temps vous avez configuré le fournisseur de test. Contrairement au véritable réseau Ethereum, qui ajoute un nouveau bloc toutes les 12 secondes environ, cette simulation attendra que vous lui donniez du travail à faire. +- `transactions` est une liste vide, pour la même raison : nous n’avons encore rien fait. Ce premier bloc est un **bloc vide**, juste pour démarrer la chaîne. +- Notez que le `parentHash` n'est qu'un ensemble d'octets vides. Cela signifie qu'il s'agit du premier bloc de la chaîne, également connu sous le nom de **bloc de genèse**. -## Arrêt #3 : Les [transactions](/developers/docs/transactions/) {#tour-stop-3-transactions} +## Arrêt n°3 : [transactions](/developers/docs/transactions/) {#tour-stop-3-transactions} -Nous sommes coincés au bloc zéro jusqu'à ce qu'il y ait une transaction en attente, alors en voilà une. Envoyez quelques ethers de test d'un compte à un autre : +Nous sommes bloqués au bloc zéro jusqu'à ce qu'il y ait une transaction en attente, alors créons-en une. Envoyez quelques ethers de test d'un compte à un autre : ```python In [10]: tx_hash = w3.eth.send_transaction({ @@ -253,11 +253,14 @@ In [10]: tx_hash = w3.eth.send_transaction({ }) ``` -C'est généralement le moment pendant lequel vous devriez attendre pendant plusieurs secondes pour que votre transaction soit réalisée et intégrée dans un nouveau bloc. Le processus complet se déroule comme ceci : +C'est généralement à ce moment-là que vous devez attendre plusieurs secondes que votre transaction soit incluse dans un nouveau bloc. Le processus complet se déroule comme suit : -1. Soumettez une transaction et attendez le hachage de la transaction. Jusqu'à ce que le bloc contenant la transaction soit créé et diffusé, la transaction sera « en attente. » `tx_hash = w3.eth.send_transaction({ … })` -2. Attendez que la transaction soit intégrée dans un bloc : `w3.eth.wait_for_transaction_receipt(tx_hash)` -3. Continuer la logique de l'application. Pour voir la transaction réussie : `w3.eth.get_transaction(tx_hash)` +1. Soumettez une transaction et conservez le hachage de la transaction. Tant que le bloc contenant la transaction n'est pas créé et diffusé, la transaction est « en attente ». + `tx_hash = w3.eth.send_transaction({ … })` +2. Attendez que la transaction soit incluse dans un bloc : + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. Poursuivre la logique de l'application. Pour afficher la transaction réussie : + `w3.eth.get_transaction(tx_hash)` Notre environnement simulé ajoutera la transaction dans un nouveau bloc instantanément, de sorte que nous pouvons immédiatement voir la transaction : @@ -274,24 +277,24 @@ Out[11]: AttributeDict({ }) ``` -Vous verrez ici quelques détails familiers : les champs `from`, `to`, et `value` doivent correspondre aux entrées de notre appel `send_transaction`. L'autre bit rassurant est que cette transaction a été incluse comme la première transaction (`'transactionIndex': 0`) dans le bloc numéro 1. +Vous verrez ici quelques détails familiers : les champs `from`, `to` et `value` doivent correspondre aux entrées de notre appel `send_transaction`. L'autre élément rassurant est que cette transaction a été incluse comme première transaction (`'transactionIndex': 0`) dans le bloc numéro 1. -Nous pouvons également facilement vérifier la réussite de cette transaction en examinant les soldes des deux comptes concernés. Trois ethers sont supposés être passés de l'un à l'autre. +Nous pouvons également vérifier facilement le succès de cette transaction en consultant les soldes des deux comptes concernés. Trois ethers auraient dû passer de l'un à l'autre. ```python -Entrée [12] : w3.eth.get_balance(w3.eth.accounts[0]) -Sortie [12]: 999996999979000000000000 +In [12]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[12]: 999996999979000000000000 -Entrée [13] : w3.eth.get_balance(w3.eth.accounts[1]) -Sortie [13] : 1000003000000000000000000 +In [13]: w3.eth.get_balance(w3.eth.accounts[1]) +Out[13]: 1000003000000000000000000 ``` -Ce dernier semble bon ! Le solde est passé de 1 000 000 à 1 000 003 ethers. Mais qu'est-il arrivé au premier compte ? Il semble avoir perdu un peu plus que trois ethers. Hélas, rien dans la vie n'est gratuit et l'utilisation du réseau public Ethereum exige que vous compensiez vos pairs pour leur rôle de soutien. Une petite commission de transaction a été déduite du compte qui a soumis la transaction - cette commission correspond à la quantité de gaz brûlé (21 000 unités de gaz pour un transfert ETH) multipliée par une commission de base qui varie en fonction de l'activité du réseau plus un pourboire qui va au validateur qui inclut la transaction dans un bloc. +Ce dernier semble bon ! Le solde est passé de 1 000 000 à 1 000 003 ethers. Mais qu'est-il arrivé au premier compte ? Il semble avoir perdu un peu plus de trois ethers. Hélas, rien dans la vie n'est gratuit, et l'utilisation du réseau public Ethereum exige que vous dédommagiez vos pairs pour leur rôle de soutien. De faibles frais de transaction ont été déduits du compte qui a soumis la transaction. Ces frais correspondent à la quantité de gaz brûlé (21 000 unités de gaz pour un transfert d'ETH) multipliée par des frais de base qui varient en fonction de l'activité du réseau, plus un pourboire qui va au validateur qui inclut la transaction dans un bloc. -En savoir plus sur les[gaz](/developers/docs/gas/#post-london) +En savoir plus sur le [gaz](/developers/docs/gas/#post-london) -Remarque : sur le réseau public, les commissions de transaction sont variables en fonction de la demande du réseau et de la rapidité avec laquelle vous souhaitez que soit traitée une transaction. Si vous êtes intéressé par la façon dont les frais sont calculés, vous pouvez consulter mon précédent article sur la manière dont les transactions sont incluses dans un bloc. +Remarque : sur le réseau public, les frais de transaction sont variables en fonction de la demande du réseau et de la rapidité avec laquelle vous souhaitez qu'une transaction soit traitée. Si vous souhaitez obtenir une répartition de la manière dont les frais sont calculés, consultez mon article précédent sur la façon dont les transactions sont incluses dans un bloc. -## Et respirez... {#and-breathe} +## Et respirez {#and-breathe} -Nous y sommes depuis un bon moment et il semble donc intéressant de faire une pause. Le terrier du lapin est toujours ouvert et nous continuerons à l'explorer dans la deuxième partie de cette série. Quelques concepts à venir : la connexion à un vrai nœud, les contrats intelligents et les jetons. Vous avez des questions complémentaires ? Faites-le moi savoir ! Vos commentaires influenceront notre chemin à partir d’ici. Vos demandes sont les bienvenues sur [Twitter](https://twitter.com/wolovim). +Cela fait un moment que nous y sommes, c'est donc le bon moment pour faire une pause. Le terrier du lapin se poursuit, et nous continuerons à l'explorer dans la deuxième partie de cette série. Quelques concepts à venir : la connexion à un nœud réel, les contrats intelligents et les jetons. Vous avez d'autres questions ? Faites-le moi savoir ! Vos commentaires influenceront la suite des événements. Les demandes sont les bienvenues via [Twitter](https://twitter.com/wolovim). diff --git a/public/content/translations/fr/developers/tutorials/all-you-can-cache/index.md b/public/content/translations/fr/developers/tutorials/all-you-can-cache/index.md index 56ee481b339..4b7baa92505 100644 --- a/public/content/translations/fr/developers/tutorials/all-you-can-cache/index.md +++ b/public/content/translations/fr/developers/tutorials/all-you-can-cache/index.md @@ -1,42 +1,39 @@ --- -title: "Tout ce qui se cache" -description: Apprenez comment créer et utiliser un contrat de mise en cache pour des transactions moins chères sur le rollup +title: "Mise en cache à volonté" +description: "Apprenez comment créer et utiliser un contrat de mise en cache pour des transactions de rollup moins chères" author: Ori Pomerantz -tags: - - "Couche 2" - - "mise en cache" - - "stockage" +tags: [ "couche 2", "mise en cache", "stockage" ] skill: intermediate published: 2022-09-15 lang: fr --- -Lors de l'utilisation de rollups, le coût d'un octet dans la transaction est bien plus élevé que le coût d'un emplacement de stockage. Par conséquent, il est logique de mettre en cache autant d'informations que possible sur la blockchain. +Lors de l'utilisation de rollups, le coût d'un octet dans la transaction est bien plus élevé que le coût d'un emplacement de stockage. Par conséquent, il est logique de mettre en cache autant d'informations que possible en chaîne. -Dans cet article, vous apprendrez comment créer et utiliser un contrat de mise en cache afin que toute valeur de paramètre susceptible d'être utilisée plusieurs fois soit mise en cache et disponible à l'utilisation (après la première fois) avec un nombre plus réduit d'octets, enfin vous apprendrez comment écrire du code qui utilise cette technique de mise en cache. +Dans cet article, vous apprendrez à créer et à utiliser un contrat de mise en cache de telle manière que toute valeur de paramètre susceptible d'être utilisée plusieurs fois soit mise en cache et disponible à l'utilisation (après la première fois) avec un nombre d'octets beaucoup plus faible, et comment écrire du code hors chaîne qui utilise ce cache. -Si vous souhaitez ignorer l'article et simplement voir le code source, vous le trouverez [ici](https://github.com/qbzzt/20220915-all-you-can-cache). Le logiciel de développement utilisé est [Foundry](https://book.getfoundry.sh/getting-started/installation). +Si vous voulez sauter l'article et voir directement le code source, [il est ici](https://github.com/qbzzt/20220915-all-you-can-cache). La pile de développement est [Foundry](https://getfoundry.sh/introduction/installation/). ## Conception générale {#overall-design} -Pour simplifier, nous supposerons que tous les paramètres de transaction sont de type `uint256`, d'une longueur de 32 octets. Lorsque nous recevons une transaction, nous analyserons chaque paramètre de la manière suivante : +Par souci de simplicité, nous supposerons que tous les paramètres de transaction sont de type `uint256` et longs de 32 octets. Lorsque nous recevons une transaction, nous analysons chaque paramètre comme ceci : 1. Si le premier octet est `0xFF`, prenez les 32 octets suivants comme valeur de paramètre et écrivez-la dans le cache. -2. Si le premier octet est `0xFE`, prenez les 32 octets suivants comme valeur de paramètre, mais _ne_ l'écrivez pas dans le cache. +2. Si le premier octet est `0xFE`, prenez les 32 octets suivants comme valeur de paramètre, mais ne l'écrivez _pas_ dans le cache. -3. Pour toute autre valeur, prenez les quatre premiers bits comme le nombre d'octets supplémentaires, et les quatre bits de fin comme les bits les plus significatifs de la clé de la mise en cache. Voici quelques exemples : +3. Pour toute autre valeur, prenez les quatre bits de poids fort comme le nombre d'octets supplémentaires, et les quatre bits de poids faible comme les bits les plus significatifs de la clé du cache. Voici quelques exemples : - | Octets dans les données d'appel | Clé de la mise en cache | - |:------------------------------- | -----------------------:| - | 0x0F | 0x0F | - | 0x10,0x10 | 0x10 | - | 0x12,0xAC | 0x02AC | - | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | + | Octets dans les données d'appel | Clé du cache | + | :------------------------------ | -----------: | + | 0x0F | 0x0F | + | 0x10,0x10 | 0x10 | + | 0x12,0xAC | 0x02AC | + | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | ## Manipulation du cache {#cache-manipulation} -La mise en cache est implémentée dans [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol). Revenons dessus ligne par ligne. +Le cache est implémenté dans [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol). Revenons dessus ligne par ligne. ```solidity // SPDX-License-Identifier: UNLICENSED @@ -49,93 +46,93 @@ contract Cache { bytes1 public constant DONT_CACHE = 0xFE; ``` -Ces constantes sont utilisées pour gérer les cas spéciaux où nous fournissons toutes les informations et souhaitons ou non les écrire dans la mise en cache. Écrire dans le cache nécessite deux opérations [`SSTORE`](https://www.evm.codes/#55) dans des emplacements de stockage précédemment libres, au coût de 22100 gaz chacune, cela est donc facultatif. +Ces constantes sont utilisées pour interpréter les cas spéciaux où nous fournissons toutes les informations et souhaitons ou non les écrire dans le cache. L'écriture dans le cache nécessite deux opérations [`SSTORE`](https://www.evm.codes/#55) dans des emplacements de stockage précédemment inutilisés, au coût de 22 100 gaz chacune, nous rendons donc cela facultatif. ```solidity mapping(uint => uint) public val2key; ``` -Une [correspondance](https://www.geeksforgeeks.org/solidity-mappings/) entre les valeurs et leurs clés. Ces informations sont nécessaires pour encoder les valeurs avant d'envoyer la transaction. +Une [correspondance](https://www.geeksforgeeks.org/solidity/solidity-mappings/) entre les valeurs et leurs clés. Ces informations sont nécessaires pour encoder les valeurs avant d'envoyer la transaction. ```solidity - // Location n has the value for key n+1, because we need to preserve - // zero as "not in the cache". + // L'emplacement n a la valeur pour la clé n+1, car nous devons préserver + // zéro comme « pas dans le cache ». uint[] public key2val; ``` -Nous pouvons utiliser un tableau pour le mappage des clés aux valeurs car nous attribuons les clés, et pour simplifier, nous le faisons de manière séquentielle. +Nous pouvons utiliser un tableau pour la correspondance des clés aux valeurs car nous attribuons les clés, et par souci de simplicité, nous le faisons de manière séquentielle. ```solidity function cacheRead(uint _key) public view returns (uint) { - require(_key <= key2val.length, "Reading uninitialize cache entry"); + require(_key <= key2val.length, "Lecture d'une entrée de cache non initialisée"); return key2val[_key-1]; } // cacheRead ``` -Lire une valeur dans le cache. +Lire une valeur à partir du cache. ```solidity - // Write a value to the cache if it's not there already - // Only public to enable the test to work + // Écrire une valeur dans le cache si elle n'y est pas déjà + // Uniquement public pour permettre au test de fonctionner function cacheWrite(uint _value) public returns (uint) { - // If the value is already in the cache, return the current key + // Si la valeur est déjà dans le cache, retourner la clé actuelle if (val2key[_value] != 0) { return val2key[_value]; } ``` -Il n'y a aucun intérêt à mettre la même valeur dans le cache plus d'une fois. Si la valeur est déjà présente, renvoyez simplement la clé existante. +Il n'y a aucun intérêt à mettre la même valeur dans le cache plus d'une fois. Si la valeur est déjà présente, il suffit de retourner la clé existante. ```solidity - // Since 0xFE is a special case, the largest key the cache can - // hold is 0x0D followed by 15 0xFF's. If the cache length is already that - // large, fail. + // Puisque 0xFE est un cas spécial, la plus grande clé que le cache peut + // contenir est 0x0D suivie de 15 0xFF. Si la longueur du cache est déjà aussi + // grande, on échoue. // 1 2 3 4 5 6 7 8 9 A B C D E F require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, - "cache overflow"); + "dépassement du cache"); ``` -Je ne pense pas que nous aurons un cache aussi grand (environ 1.8\*1037 valeurs, ce qui nécessiterait environ 1027 To de stockage). Cependant, je suis assez vieux pour me rappeler que ["640 ko sera toujours suffisant"](https://quoteinvestigator.com/2011/09/08/640k-enough/). Ce test est très peu coûteux. +Je ne pense pas que nous aurons jamais un cache aussi grand (environ 1,8\*1037 entrées, ce qui nécessiterait environ 1027 To de stockage). Cependant, je suis assez vieux pour me souvenir du fameux [« 640 Ko devraient suffire à tout le monde »](https://quoteinvestigator.com/2011/09/08/640k-enough/). Ce test est très peu coûteux. ```solidity - // Write the value using the next key + // Écrire la valeur en utilisant la clé suivante val2key[_value] = key2val.length+1; ``` -Ajoutez la recherche inversée (trouver la clé en fonction de la valeur). +Ajoutez la recherche inversée (de la valeur à la clé). ```solidity key2val.push(_value); ``` -Ajoutez la recherche directe (trouver la valeur en fonction de la clé). Comme nous attribuons des valeurs de manière séquentielle, nous pouvons simplement l'ajouter après la dernière valeur du tableau. +Ajoutez la recherche directe (de la clé à la valeur). Comme nous attribuons des valeurs de manière séquentielle, nous pouvons simplement l'ajouter après la dernière valeur du tableau. ```solidity return key2val.length; - } // cacheWrite + } // écritureCache ``` -Renvoyez la nouvelle longueur de `key2val`, qui est la cellule où la nouvelle valeur est stockée. +Retourner la nouvelle longueur de `key2val`, qui est la cellule où la nouvelle valeur est stockée. ```solidity function _calldataVal(uint startByte, uint length) private pure returns (uint) ``` -Cette fonction lit une valeur à partir du calldata d'une longueur arbitraire (jusqu'à 32 octets, la taille d'un mot). +Cette fonction lit une valeur à partir des données d'appel, de longueur arbitraire (jusqu'à 32 octets, la taille d'un mot). ```solidity { uint _retVal; require(length < 0x21, - "_calldataVal length limit is 32 bytes"); + "la limite de longueur de _calldataVal est de 32 octets"); require(length + startByte <= msg.data.length, - "_calldataVal trying to read beyond calldatasize"); + "_calldataVal tente de lire au-delà de calldatasize"); ``` -Cette fonction est interne, donc si le reste du code est écrit correctement, ces tests ne sont pas nécessaires. Cependant, ils ne coûtent pas grand-chose, donc autant les inclure. +Cette fonction est interne, donc si le reste du code est écrit correctement, ces tests ne sont pas nécessaires. Cependant, ils ne coûtent pas cher, alors autant les avoir. ```solidity assembly { @@ -143,56 +140,56 @@ Cette fonction est interne, donc si le reste du code est écrit correctement, ce } ``` -Ce code est en [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html). Il lit une valeur de 32 octets à partir du calldata. Cela fonctionne même si le calldata s'arrête avant `startByte+32` car l'espace non initialisé dans l'EVM est considéré comme nul. +Ce code est en [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html). Il lit une valeur de 32 octets à partir des données d'appel. Cela fonctionne même si les données d'appel s'arrêtent avant `startByte+32`, car l'espace non initialisé dans l'EVM est considéré comme étant nul. ```solidity _retVal = _retVal >> (256-length*8); ``` -Nous n'avons pas nécessairement besoin d'une valeur de 32 octets. Cela élimine les octets en trop. +Nous n'avons pas nécessairement besoin d'une valeur de 32 octets. Cela permet de se débarrasser des octets excédentaires. ```solidity return _retVal; } // _calldataVal - // Read a single parameter from the calldata, starting at _fromByte + // Lire un seul paramètre depuis calldata, en commençant à _fromByte function _readParam(uint _fromByte) internal returns (uint _nextByte, uint _parameterValue) { ``` -Lire un seul paramètre à partir du calldata. Notez que nous devons retourner non seulement la valeur que nous avons lue, mais également l'emplacement de l'octet suivant, car les paramètres peuvent avoir une longueur allant d'un octet à 33 octets. +Lire un seul paramètre à partir des données d'appel. Notez que nous devons retourner non seulement la valeur que nous avons lue, mais aussi l'emplacement de l'octet suivant, car les paramètres peuvent avoir une longueur allant de 1 à 33 octets. ```solidity - // The first byte tells us how to interpret the rest + // Le premier octet nous indique comment interpréter le reste uint8 _firstByte; _firstByte = uint8(_calldataVal(_fromByte, 1)); ``` -Solidity s'efforce de réduire le nombre de bugs en interdisant les [conversions implicites](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions) car potentiellement dangereuses. Une réduction, par exemple de 256 bits à 8 bits, doit être explicitement indiquée. +Solidity essaie de réduire le nombre de bogues en interdisant les [conversions de type implicites](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions) potentiellement dangereuses. Une conversion descendante, par exemple de 256 bits à 8 bits, doit être explicite. ```solidity - // Read the value, but do not write it to the cache + // Lire la valeur, mais ne pas l'écrire dans le cache if (_firstByte == uint8(DONT_CACHE)) return(_fromByte+33, _calldataVal(_fromByte+1, 32)); - // Read the value, and write it to the cache + // Lire la valeur et l'écrire dans le cache if (_firstByte == uint8(INTO_CACHE)) { uint _param = _calldataVal(_fromByte+1, 32); cacheWrite(_param); return(_fromByte+33, _param); } - // If we got here it means that we need to read from the cache + // Si nous arrivons ici, cela signifie que nous devons lire depuis le cache - // Number of extra bytes to read + // Nombre d'octets supplémentaires à lire uint8 _extraBytes = _firstByte / 16; ``` -Prendre le [nibble](https://fr.wikipedia.org/wiki/Nibble) inférieur et le combiner avec les autres octets pour lire la valeur depuis le cache. +Prenez le [quartet](https://fr.wikipedia.org/wiki/Quartet_\(informatique\)) inférieur et combinez-le avec les autres octets pour lire la valeur du cache. ```solidity uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) + @@ -203,17 +200,17 @@ Prendre le [nibble](https://fr.wikipedia.org/wiki/Nibble) inférieur et le combi } // _readParam - // Read n parameters (functions know how many parameters they expect) + // Lire n paramètres (les fonctions savent combien de paramètres elles attendent) function _readParams(uint _paramNum) internal returns (uint[] memory) { ``` -Nous pourrions obtenir le nombre de paramètres que nous avons à partir du calldata en lui-même, mais les fonctions qui nous appellent connaissent le nombre de paramètres qu'elles attendent. Il est plus facile de les laisser faire. +Nous pourrions obtenir le nombre de paramètres à partir des données d'appel elles-mêmes, mais les fonctions qui nous appellent savent combien de paramètres elles attendent. Il est plus simple de les laisser nous le dire. ```solidity - // The parameters we read + // Les paramètres que nous lisons uint[] memory params = new uint[](_paramNum); - // Parameters start at byte 4, before that it's the function signature + // Les paramètres commencent à l'octet 4, avant cela se trouve la signature de la fonction uint _atByte = 4; for(uint i=0; i<_paramNum; i++) { @@ -221,14 +218,14 @@ Nous pourrions obtenir le nombre de paramètres que nous avons à partir du call } ``` -Lisez les paramètres jusqu'à obtenir le nombre requis. Si nous dépassons la fin du calldata, `_readParams` annulera l'appel. +Lisez les paramètres jusqu'à ce que vous ayez le nombre requis. Si nous dépassons la fin des données d'appel, `_readParams` annulera l'appel. ```solidity return(params); } // readParams - // For testing _readParams, test reading four parameters + // Pour tester _readParams, on teste la lecture de quatre paramètres function fourParam() public returns (uint256,uint256,uint256,uint256) { @@ -238,45 +235,45 @@ Lisez les paramètres jusqu'à obtenir le nombre requis. Si nous dépassons la f } // fourParam ``` -Un grand avantage de Foundry est qu'il permet d'écrire des tests en Solidity ([voir le test du cache ci-dessous](#testing-the-cache)). Cela facilite grandement les tests unitaires. Voici une fonction qui lit quatre paramètres et les renvoie afin que le test puisse vérifier s'ils étaient corrects. +Un grand avantage de Foundry est qu'il permet d'écrire des tests en Solidity ([voir Tester le cache ci-dessous](#testing-the-cache)). Cela facilite grandement les tests unitaires. Il s'agit d'une fonction qui lit quatre paramètres et les retourne pour que le test puisse vérifier s'ils étaient corrects. ```solidity - // Get a value, return bytes that will encode it (using the cache if possible) + // Obtenir une valeur, retourner les octets qui l'encoderont (en utilisant le cache si possible) function encodeVal(uint _val) public view returns(bytes memory) { ``` -`encodeVal` est une fonction qui appelle du code hors chaîne pour aider à créer des données d'appel qui utilisent le cache. Elle reçoit une seule valeur et renvoie les octets qui l'encodent. Cette fonction est une `lecture`, donc elle ne nécessite pas de transaction et lorsqu'elle est appelée de manière externe, elle ne coûte aucun gaz. +`encodeVal` est une fonction que le code hors chaîne appelle pour aider à créer des données d'appel qui utilisent le cache. Elle reçoit une seule valeur et retourne les octets qui l'encodent. Cette fonction est une `vue`, elle ne nécessite donc pas de transaction et, lorsqu'elle est appelée de l'extérieur, ne coûte pas de gaz. ```solidity uint _key = val2key[_val]; - // The value isn't in the cache yet, add it + // La valeur n'est pas encore dans le cache, on l'ajoute if (_key == 0) return bytes.concat(INTO_CACHE, bytes32(_val)); ``` -Dans l'[EVM](/developers/docs/evm/), toute zone de stockage non initialisée est considérée comme valant zéro. Ainsi, si nous recherchons la clé d'une valeur qui n'existe pas, nous obtenons zéro. Dans ce cas, les octets qui l'encodent sont dans `INTO_CACHE` (afin qu'elle soit mise en cache la prochaine fois), suivis de la valeur réelle. +Dans l'[EVM](/developers/docs/evm/), tout le stockage non initialisé est supposé être nul. Donc, si nous cherchons la clé d'une valeur qui n'est pas là, nous obtenons un zéro. Dans ce cas, les octets qui l'encodent sont `INTO_CACHE` (afin qu'elle soit mise en cache la prochaine fois), suivis de la valeur réelle. ```solidity - // If the key is <0x10, return it as a single byte + // Si la clé est <0x10, on la retourne comme un seul octet if (_key < 0x10) return bytes.concat(bytes1(uint8(_key))); ``` -Les simples octets sont les plus simples. Nous utilisons simplement [bytes.concat](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat) pour convertir un type `bytes` en un tableau d'octets de n'importe quelle longueur. Malgré le nom, cela fonctionne quand même lorsqu'un seul argument est fourni. +Les octets uniques sont les plus simples. Nous utilisons simplement [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat) pour transformer un type `bytes` en un tableau d'octets de n'importe quelle longueur. Malgré son nom, cela fonctionne bien lorsqu'on ne lui fournit qu'un seul argument. ```solidity - // Two byte value, encoded as 0x1vvv + // Valeur sur deux octets, encodée comme 0x1vvv if (_key < 0x1000) return bytes.concat(bytes2(uint16(_key) | 0x1000)); ``` -Lorsque nous avons une valeur inférieure à 163, nous pouvons l'exprimer en seulement deux octets. Nous convertissons d'abord `_key`, qui est une valeur de 256 bits, en une valeur de 16 bits et utilisons un calcul logique pour ajouter le nombre d'octets au premier octet. Ensuite, nous le convertissons en une valeur `bytes2`, qui peut être convertie en `bytes`. +Lorsque nous avons une clé inférieure à 163, nous pouvons l'exprimer en deux octets. Nous convertissons d'abord `_key`, qui est une valeur de 256 bits, en une valeur de 16 bits et utilisons un OU logique pour ajouter le nombre d'octets supplémentaires au premier octet. Ensuite, nous la convertissons en une valeur `bytes2`, qui peut être convertie en `bytes`. ```solidity - // There is probably a clever way to do the following lines as a loop, - // but it's a view function so I'm optimizing for programmer time and - // simplicity. + // Il existe probablement une manière astucieuse de faire les lignes suivantes en boucle, + // mais c'est une fonction de vue, donc j'optimise le temps du programmeur et + // la simplicité. if (_key < 16*256**2) return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2))); @@ -291,14 +288,14 @@ Lorsque nous avons une valeur inférieure à 163, nous pouvons l'expr return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15))); ``` -Les autres possibilités (3 octets, 4 octets, etc.) sont gérées de la même manière, mais avec des tailles de champ différentes. +Les autres valeurs (3 octets, 4 octets, etc.) sont traitées de la même manière, mais avec des tailles de champ différentes. ```solidity - // If we get here, something is wrong. - revert("Error in encodeVal, should not happen"); + // Si nous arrivons ici, quelque chose ne va pas. + revert("Erreur dans encodeVal, ne devrait pas se produire"); ``` -Si nous en arrivons là, cela signifie que nous avons obtenu une valeur qui n'est pas inférieure à 16 * 25615. Mais `cacheWrite` limite les clés donc nous ne pouvons même pas atteindre 14\*25616 (ce qui aurait un premier octet de 0xFE, donc cela ressemblerait à `DONT_CACHE`). Mais cela ne nous coûte pas beaucoup d'ajouter un test au cas où un futur programmeur introduirait un bug. +Si nous arrivons ici, cela signifie que nous avons obtenu une clé qui n'est pas inférieure à 16\*25615. Mais `cacheWrite` limite les clés, donc nous ne pouvons même pas atteindre 14\*25616 (ce qui donnerait un premier octet de 0xFE, et ressemblerait donc à `DONT_CACHE`). Mais cela ne coûte pas cher d'ajouter un test au cas où un futur programmeur introduirait un bogue. ```solidity } // encodeVal @@ -308,7 +305,7 @@ Si nous en arrivons là, cela signifie que nous avons obtenu une valeur qui n'es ### Tester le cache {#testing-the-cache} -L'un des avantages de Foundry est qu'[il vous permet d'écrire des tests en Solidity](https://book.getfoundry.sh/forge/tests), ce qui facilite l'écriture de tests de type. Les tests pour la classe `Cache` se trouvent [ici](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol). Comme le code de test est répétitif, comme c'est souvent le cas avec les tests, cet article n'explique que les parties utiles. +L'un des avantages de Foundry est qu'[il permet d'écrire des tests en Solidity](https://getfoundry.sh/forge/tests/overview/), ce qui facilite l'écriture de tests unitaires. Les tests pour la classe `Cache` sont [ici](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol). Le code de test étant répétitif, comme c'est souvent le cas, cet article n'explique que les parties intéressantes. ```solidity // SPDX-License-Identifier: UNLICENSED @@ -317,17 +314,17 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; -// Need to run `forge test -vv` for the console. +// Il faut lancer `forge test -vv` pour la console. import "forge-std/console.sol"; ``` -Il s'agit simplement d'un passe-partout nécessaire pour utiliser le package de test et `console.log`. +Ceci est juste du code standard nécessaire pour utiliser le paquet de test et `console.log`. ```solidity import "src/Cache.sol"; ``` -Nous avons besoin de connaître le contrat que nous testons. +Nous devons connaître le contrat que nous testons. ```solidity contract CacheTest is Test { @@ -344,7 +341,7 @@ La fonction `setUp` est appelée avant chaque test. Dans ce cas, nous créons si function testCaching() public { ``` -Les tests sont des fonctions dont les noms commencent par `test`. Cette fonction vérifie la fonctionnalité de base du cache, en écrivant des valeurs puis en les lisant à nouveau. +Les tests sont des fonctions dont le nom commence par `test`. Cette fonction vérifie la fonctionnalité de base du cache, en écrivant des valeurs et en les relisant. ```solidity for(uint i=1; i<5000; i++) { @@ -355,15 +352,15 @@ Les tests sont des fonctions dont les noms commencent par `test`. Cette fonction assertEq(cache.cacheRead(i), i*i); ``` -C'est ainsi que vous effectuez les tests réels en utilisant [ les fonctions `assert...`](https://book.getfoundry.sh/reference/forge-std/std-assertions). Dans ce cas, nous vérifions que la valeur que nous avons écrite est celle que nous avons lue. Nous pouvons ignorer le résultat de `cache.cacheWrite` car nous savons que les valeurs de cache sont assignées linéairement. +C'est ainsi que vous effectuez les tests réels, en utilisant les [`fonctions assert...`](https://getfoundry.sh/reference/forge-std/std-assertions/). Dans ce cas, nous vérifions que la valeur que nous avons écrite est bien celle que nous avons lue. Nous pouvons ignorer le résultat de `cache.cacheWrite` car nous savons que les clés de cache sont attribuées de manière linéaire. ```solidity } } // testCaching - // Cache the same value multiple times, ensure that the key stays - // the same + // Mettre en cache la même valeur plusieurs fois, s'assurer que la clé reste + // la même function testRepeatCaching() public { for(uint i=1; i<100; i++) { uint _key1 = cache.cacheWrite(i); @@ -372,7 +369,7 @@ C'est ainsi que vous effectuez les tests réels en utilisant [ les fonctions `as } ``` -Tout d'abord, nous écrivons chaque valeur deux fois dans le cache et nous nous assurons que les valeurs sont les mêmes (ce qui signifie que la deuxième écriture ne s'est pas réellement produite). +D'abord, nous écrivons chaque valeur deux fois dans le cache et nous nous assurons que les clés sont les mêmes (ce qui signifie que la deuxième écriture n'a pas vraiment eu lieu). ```solidity for(uint i=1; i<100; i+=3) { @@ -382,16 +379,16 @@ Tout d'abord, nous écrivons chaque valeur deux fois dans le cache et nous nous } // testRepeatCaching ``` -En théorie, il pourrait y avoir un bug qui n'affecte pas les écritures consécutives dans le cache. Ici, nous effectuons donc quelques écritures qui ne sont pas consécutives et vérifions que les valeurs ne sont toujours pas réécrites. +En théorie, il pourrait y avoir un bogue qui n'affecte pas les écritures consécutives dans le cache. Ici, nous effectuons donc des écritures non consécutives et nous voyons que les valeurs ne sont toujours pas réécrites. ```solidity - // Read a uint from a memory buffer (to make sure we get back the parameters - // we sent out) + // Lire un uint à partir d'un tampon mémoire (pour s'assurer de récupérer les paramètres + // que nous avons envoyés) function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) ``` -Lire un mot de 256 bits à partir de la `mémoire tampon de bytes`. Cette fonction utilitaire nous permet de vérifier que nous obtenons les résultats corrects lorsque nous exécutons un appel de fonction qui utilise le cache. +Lire un mot de 256 bits à partir d'un tampon `bytes memory`. Cette fonction utilitaire nous permet de vérifier que nous recevons les résultats corrects lorsque nous exécutons un appel de fonction qui utilise le cache. ```solidity { @@ -403,18 +400,18 @@ Lire un mot de 256 bits à partir de la `mémoire tampon de bytes`. Cette foncti } ``` -Yul ne prend pas en charge les structures de données plus avancées que `uint256`, ainsi lorsque vous faites référence à une structure de données plus sophistiquée, telle que la mémoire tampon `_bytes`, vous obtenez l'adresse de cette structure. Solidity stocke les valeurs de `bytes memory` sous la forme d'un mot de 32 octets qui contient la longueur, suivie des octets réels. Par conséquent, pour obtenir l'octet numéro `_start`, nous devons calculer `_bytes+32+_start`. +Yul ne prend pas en charge les structures de données au-delà de `uint256`, donc lorsque vous faites référence à une structure de données plus sophistiquée, telle que le tampon mémoire `_bytes`, vous obtenez l'adresse de cette structure. Solidity stocke les valeurs `bytes memory` sous la forme d'un mot de 32 octets qui contient la longueur, suivi des octets réels. Pour obtenir l'octet numéro `_start`, nous devons donc calculer `_bytes+32+_start`. ```solidity return tempUint; } // toUint256 - // Function signature for fourParams(), courtesy of + // Signature de la fonction pour fourParams(), grâce à // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d bytes4 constant FOUR_PARAMS = 0x3edc1e6d; - // Just some constant values to see we're getting the correct values back + // Juste quelques valeurs constantes pour voir si nous obtenons les bonnes valeurs en retour uint256 constant VAL_A = 0xDEAD60A7; uint256 constant VAL_B = 0xBEEF; uint256 constant VAL_C = 0x600D; @@ -427,7 +424,7 @@ Quelques constantes dont nous avons besoin pour les tests. function testReadParam() public { ``` -Utilisez `fourParams()`, une fonction qui utilise `readParams`, pour tester si nous pouvons lire correctement les paramètres. +Appelez `fourParams()`, une fonction qui utilise `readParams`, pour tester que nous pouvons lire les paramètres correctement. ```solidity address _cacheAddr = address(cache); @@ -436,23 +433,23 @@ Utilisez `fourParams()`, une fonction qui utilise `readParams`, pour tester si n bytes memory _callOutput; ``` -Nous ne pouvons pas utiliser le mécanisme ABI normal pour appeler une fonction en utilisant le cache, nous devons donc utiliser le mécanisme [`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses) de bas niveau. Ce mécanisme prend une `mémoire bytes` en entrée et renvoie celle-ci (ainsi qu'une valeur booléenne) en sortie. +Nous ne pouvons pas utiliser le mécanisme ABI normal pour appeler une fonction en utilisant le cache, nous devons donc utiliser le mécanisme de bas niveau [`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses). Ce mécanisme prend un `bytes memory` en entrée et le retourne (ainsi qu'une valeur booléenne) en sortie. ```solidity - // First call, the cache is empty + // Premier appel, le cache est vide _callInput = bytes.concat( FOUR_PARAMS, ``` -Il est utile que le même contrat prenne en charge à la fois les fonctions mises en cache (pour les appels directement à partir de transactions) et les fonctions non mises en cache (pour les appels à partir d'autres contrats intelligents). Pour ce faire, nous devons continuer à nous appuyer sur le mécanisme Solidity pour appeler la bonne fonction, au lieu de tout mettre dans une [`fonction de repli`](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function). Cela rend la composabilité beaucoup plus facile. Un seul octet serait suffisant pour identifier la fonction dans la plupart des cas, nous gaspillons donc trois octets (16\*3=48 gaz). Cependant, au moment où j'écris cela, ces 48 gaz coûtent 0,07 cent, ce qui est un coût raisonnable pour un code plus simple et moins sujet aux bugs. +Il est utile que le même contrat prenne en charge à la fois les fonctions mises en cache (pour les appels directement depuis les transactions) et les fonctions non mises en cache (pour les appels depuis d'autres contrats intelligents). Pour ce faire, nous devons continuer à nous appuyer sur le mécanisme de Solidity pour appeler la bonne fonction, au lieu de tout mettre dans [une fonction de repli (`fallback`)](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function). Cela rend la composabilité beaucoup plus facile. Un seul octet suffirait pour identifier la fonction dans la plupart des cas, nous gaspillons donc trois octets (16\*3=48 gaz). Cependant, au moment où j'écris ces lignes, ces 48 gaz coûtent 0,07 centime, ce qui est un coût raisonnable pour un code plus simple et moins sujet aux bogues. ```solidity - // First value, add it to the cache + // Première valeur, on l'ajoute au cache cache.INTO_CACHE(), bytes32(VAL_A), ``` -La première valeur : un indicateur indiquant qu'il s'agit d'une valeur qui doit être écrite en entier dans le cache, suivie de ses 32 octets. Les trois autres valeurs sont similaires, sauf que `VAL_B` n'est pas écrite dans le cache et que `VAL_C` est à la fois le troisième paramètre et le quatrième. +La première valeur : un indicateur signalant qu'il s'agit d'une valeur complète qui doit être écrite dans le cache, suivi des 32 octets de la valeur. Les trois autres valeurs sont similaires, sauf que `VAL_B` n'est pas écrit dans le cache et que `VAL_C` est à la fois le troisième et le quatrième paramètre. ```solidity . @@ -468,14 +465,14 @@ C'est ici que nous appelons réellement le contrat `Cache`. assertEq(_success, true); ``` -Nous nous attendons à ce que l'appel soit réussi. +Nous nous attendons à ce que l'appel réussisse. ```solidity assertEq(cache.cacheRead(1), VAL_A); assertEq(cache.cacheRead(2), VAL_C); ``` -Nous commençons avec un cache vide, puis ajoutons `VAL_A` suivi de `VAL_C`. Nous nous attendrions à ce que le premier ait la valeur 1 et le second la valeur 2. +Nous commençons avec un cache vide, puis nous ajoutons `VAL_A` suivi de `VAL_C`. Nous nous attendons à ce que le premier ait la clé 1 et le second la clé 2. ``` assertEq(toUint256(_callOutput,0), VAL_A); @@ -484,32 +481,31 @@ Nous commençons avec un cache vide, puis ajoutons `VAL_A` suivi de `VAL_C`. Nou assertEq(toUint256(_callOutput,96), VAL_C); ``` -Le résultat est composé des quatre paramètres. Ici, nous vérifions qu'il est correct. +La sortie correspond aux quatre paramètres. Ici, nous vérifions qu'elle est correcte. ```solidity - // Second call, we can use the cache + // Deuxième appel, nous pouvons utiliser le cache _callInput = bytes.concat( FOUR_PARAMS, - // First value in the Cache + // Première valeur dans le cache bytes1(0x01), ``` -Les clés de cache inférieures à 16 ne représentent qu'un octet. +Les clés de cache inférieures à 16 ne représentent qu'un seul octet. ```solidity - // Second value, don't add it to the cache + // Deuxième valeur, ne pas l'ajouter au cache cache.DONT_CACHE(), bytes32(VAL_B), - // Third and fourth values, same value + // Troisième et quatrième valeurs, même valeur bytes1(0x02), bytes1(0x02) ); . . . - } // testReadParam ``` Les tests après l'appel sont identiques à ceux après le premier appel. @@ -518,7 +514,7 @@ Les tests après l'appel sont identiques à ceux après le premier appel. function testEncodeVal() public { ``` -Cette fonction est similaire à `testReadParam`, à la différence près que nous utilisons `encodeVal()` au lieu d'écrire explicitement les paramètres. +Cette fonction est similaire à `testReadParam`, sauf qu'au lieu d'écrire les paramètres explicitement, nous utilisons `encodeVal()`. ```solidity . @@ -538,23 +534,23 @@ Cette fonction est similaire à `testReadParam`, à la différence près que nou } // testEncodeVal ``` -Le seul test supplémentaire dans `testEncodeVal()` consiste à vérifier que la longueur de `_callInput` est correcte. Pour le premier appel, elle est de 4+33\*4. Pour le deuxième, où chaque valeur est déjà dans le cache, elle est de 4+1\*4. +Le seul test supplémentaire dans `testEncodeVal()` consiste à vérifier que la longueur de `_callInput` est correcte. Pour le premier appel, elle est de 4+33\*4. Pour le second, où chaque valeur est déjà dans le cache, elle est de 4+1\*4. ```solidity - // Test encodeVal when the key is more than a single byte - // Maximum three bytes because filling the cache to four bytes takes - // too long. + // Tester encodeVal lorsque la clé fait plus d'un seul octet + // Maximum trois octets, car remplir le cache jusqu'à quatre octets prend + // trop de temps. function testEncodeValBig() public { - // Put a number of values in the cache. - // To keep things simple, use key n for value n. + // Mettre un certain nombre de valeurs dans le cache. + // Pour rester simple, utiliser la clé n pour la valeur n. for(uint i=1; i<0x1FFF; i++) { cache.cacheWrite(i); } ``` -La fonction `testEncodeVal` ci-dessus ne stocke que quatre valeurs dans le cache, donc [la partie de la fonction qui traite des valeurs multi-octets](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171) n'est pas vérifiée. Mais ce code est compliqué et particulièrement sujet aux erreurs. +La fonction `testEncodeVal` ci-dessus n'écrit que quatre valeurs dans le cache, donc [la partie de la fonction qui traite les valeurs multi-octets](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171) n'est pas vérifiée. Mais ce code est compliqué et sujet aux erreurs. -La première partie de cette fonction est une boucle qui écrit toutes les valeurs de 1 à 0x1FFF dans le cache en ordre, afin que nous puissions encoder ces valeurs et savoir où elles iront. +La première partie de cette fonction est une boucle qui écrit toutes les valeurs de 1 à 0x1FFF dans le cache, dans l'ordre, afin que nous puissions encoder ces valeurs et savoir où elles vont. ```solidity . @@ -563,14 +559,14 @@ La première partie de cette fonction est une boucle qui écrit toutes les valeu _callInput = bytes.concat( FOUR_PARAMS, - cache.encodeVal(0x000F), // One byte 0x0F - cache.encodeVal(0x0010), // Two bytes 0x1010 - cache.encodeVal(0x0100), // Two bytes 0x1100 - cache.encodeVal(0x1000) // Three bytes 0x201000 + cache.encodeVal(0x000F), // Un octet 0x0F + cache.encodeVal(0x0010), // Deux octets 0x1010 + cache.encodeVal(0x0100), // Deux octets 0x1100 + cache.encodeVal(0x1000) // Trois octets 0x201000 ); ``` -Testez des valeurs d'un octet, de deux octets et de trois octets. Nous n'allons pas au-delà car cela prendrait trop de temps pour écrire suffisamment d'entrées dans la pile (au moins 0x10000000, soit environ un quart de milliard). +Testez les valeurs d'un, deux et trois octets. Nous ne testons pas au-delà car cela prendrait trop de temps pour écrire suffisamment d'entrées dans la pile (au moins 0x10000000, soit environ un quart de milliard). ```solidity . @@ -580,11 +576,11 @@ Testez des valeurs d'un octet, de deux octets et de trois octets. Nous n'allons } // testEncodeValBig - // Test what with an excessively small buffer we get a revert + // Tester qu'avec un tampon excessivement petit, nous obtenons une annulation function testShortCalldata() public { ``` -Testez ce qui se passe dans le cas anormal où il n'y a pas suffisamment de paramètres. +Testez ce qui se passe dans le cas anormal où il n'y a pas assez de paramètres. ```solidity . @@ -595,10 +591,10 @@ Testez ce qui se passe dans le cas anormal où il n'y a pas suffisamment de para } // testShortCalldata ``` -Puisqu'il revient, le résultat que nous devrions obtenir est `false`. +Puisqu'il y a annulation, le résultat que nous devrions obtenir est `false`. ``` - // Call with cache keys that aren't there + // Appeler avec des clés de cache qui ne sont pas là function testNoCacheKey() public { . . @@ -606,52 +602,52 @@ Puisqu'il revient, le résultat que nous devrions obtenir est `false`. _callInput = bytes.concat( FOUR_PARAMS, - // First value, add it to the cache + // Première valeur, l'ajouter au cache cache.INTO_CACHE(), bytes32(VAL_A), - // Second value + // Deuxième valeur bytes1(0x0F), bytes2(0x1234), bytes11(0xA10102030405060708090A) ); ``` -Cette fonction obtient quatre paramètres parfaitement légitimes, à l'exception du fait que le cache est vide, il n'y a donc aucune valeur à lire. +Cette fonction reçoit quatre paramètres parfaitement légitimes, sauf que le cache est vide, donc il n'y a aucune valeur à lire. ```solidity . . . - // Test what with an excessively long buffer everything works file + // Tester qu'avec un tampon excessivement long tout fonctionne function testLongCalldata() public { address _cacheAddr = address(cache); bool _success; bytes memory _callInput; bytes memory _callOutput; - // First call, the cache is empty + // Premier appel, le cache est vide _callInput = bytes.concat( FOUR_PARAMS, - // First value, add it to the cache + // Première valeur, l'ajouter au cache cache.INTO_CACHE(), bytes32(VAL_A), - // Second value, add it to the cache + // Deuxième valeur, l'ajouter au cache cache.INTO_CACHE(), bytes32(VAL_B), - // Third value, add it to the cache + // Troisième valeur, l'ajouter au cache cache.INTO_CACHE(), bytes32(VAL_C), - // Fourth value, add it to the cache + // Quatrième valeur, l'ajouter au cache cache.INTO_CACHE(), bytes32(VAL_D), - // And another value for "good luck" + // Et une autre valeur pour la « bonne chance » bytes4(0x31112233) ); ``` -Cette fonction envoie cinq valeurs. Nous savons que la cinquième valeur est ignorée car ce n'est pas une entrée de cache valide, ce qui aurait provoqué un rejet si elle n'avait pas été incluse. +Cette fonction envoie cinq valeurs. Nous savons que la cinquième valeur est ignorée car ce n'est pas une entrée de cache valide, ce qui aurait provoqué une annulation si elle n'avait pas été incluse. ```solidity (_success, _callOutput) = _cacheAddr.call(_callInput); @@ -665,13 +661,13 @@ Cette fonction envoie cinq valeurs. Nous savons que la cinquième valeur est ign ``` -## Exemple d'application {#a-sample-app} +## Un exemple d'application {#a-sample-app} -Écrire des tests en Solidity c'est très bien, mais en fin de compte, une DApp doit être capable de traiter les demandes venant de la chaîne pour être utile. Cet article montre comment utiliser la mise en cache dans une DApp avec `WORM`, qui signifie « Écrire Une Fois, Lire Plusieurs fois » (en anglais "Write Once, Read Many"). Si une clé n'a pas encore été assignée, vous pouvez y écrire une valeur. Si la clé a déjà été assignée, cela annule l'écriture. +Écrire des tests en Solidity est très bien, mais au final, une dapp doit être capable de traiter des requêtes provenant de l'extérieur de la chaîne pour être utile. Cet article montre comment utiliser la mise en cache dans une dapp avec `WORM`, qui signifie « Write Once, Read Many » (Écrire une fois, lire plusieurs fois). Si une clé n'est pas encore écrite, vous pouvez y écrire une valeur. Si la clé est déjà écrite, vous obtenez une annulation. ### Le contrat {#the-contract} -[Voici le contrat](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol). Cela répète en grande partie ce que nous avons déjà fait avec `Cache` et `CacheTest`, nous nous concentrons donc uniquement sur les parties intéressantes. +[Voici le contrat](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol). Il répète en grande partie ce que nous avons déjà fait avec `Cache` et `CacheTest`, nous ne couvrirons donc que les parties intéressantes. ```solidity import "./Cache.sol"; @@ -688,29 +684,29 @@ La manière la plus simple d'utiliser `Cache` est de l'hériter dans notre propr } // writeEntryCached ``` -Cette fonction est similaire à `fourParam` dans `CacheTest` ci-dessus. Comme nous ne suivons pas les spécifications ABI, il est préférable de ne pas déclarer de paramètres dans la fonction. +Cette fonction est similaire à `fourParam` dans `CacheTest` ci-dessus. Comme nous ne suivons pas les spécifications de l'ABI, il est préférable de ne déclarer aucun paramètre dans la fonction. ```solidity - // Make it easier to call us - // Function signature for writeEntryCached(), courtesy of + // Pour faciliter les appels + // Signature de la fonction pour writeEntryCached(), grâce à // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3 bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3; ``` -Le code externe qui appelle `writeEntryCached` devra construire manuellement les données d'appel, au lieu d'utiliser `worm.writeEntryCached`, car nous ne suivons pas les spécifications ABI. Avoir cette valeur constante rend plus facile son écriture. +Le code externe qui appelle `writeEntryCached` devra construire manuellement les données d'appel, au lieu d'utiliser `worm.writeEntryCached`, car nous ne suivons pas les spécifications de l'ABI. Avoir cette valeur constante facilite simplement son écriture. -Notez que même si nous définissons `WRITE_ENTRY_CACHED` comme une variable d'état, pour la lire de l'extérieur, il est nécessaire d'utiliser la fonction getter pour cela, `worm.WRITE_ENTRY_CACHED()`. +Notez que même si nous définissons `WRITE_ENTRY_CACHED` comme une variable d'état, pour la lire de l'extérieur, il est nécessaire d'utiliser sa fonction d'accès, `worm.WRITE_ENTRY_CACHED()`. ```solidity function readEntry(uint key) public view returns (uint _value, address _writtenBy, uint _writtenAtBlock) ``` -La fonction de lecture est en `lecture`, elle ne nécessite donc pas de transaction et ne coûte pas de gaz. En conséquence, il n'y a aucun avantage à utiliser le cache pour le paramètre. Avec les fonctions de type en lecture, il est préférable d'utiliser le mécanisme standard qui est plus simple. +La fonction de lecture est une `vue`, elle ne nécessite donc pas de transaction et ne coûte pas de gaz. Par conséquent, il n'y a aucun avantage à utiliser le cache pour le paramètre. Avec les fonctions de vue, il est préférable d'utiliser le mécanisme standard qui est plus simple. -### Code de test {#the-testing-code} +### Le code de test {#the-testing-code} -[Voici le code de test pour le contrat](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol). Encore une fois, regardons uniquement ce qui est intéressant. +[Voici le code de test du contrat](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol). Encore une fois, ne regardons que ce qui est intéressant. ```solidity function testWReadWrite() public { @@ -720,27 +716,27 @@ La fonction de lecture est en `lecture`, elle ne nécessite donc pas de transact worm.writeEntry(0xDEAD, 0xBEEF); ``` -[Ceci (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) est la façon dont nous spécifions dans un test Foundry que le prochain appel devrait échouer, et la raison de cet échec. Cela s'applique lorsque nous utilisons la syntaxe `.()` plutôt que de construire les données d'appel et d'appeler le contrat en utilisant l'interface de bas niveau (`.call()`, etc.). +[Ceci (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) est la façon dont nous spécifions dans un test Foundry que l'appel suivant doit échouer, ainsi que la raison de l'échec. Cela s'applique lorsque nous utilisons la syntaxe `.()` plutôt que de construire les données d'appel et d'appeler le contrat en utilisant l'interface de bas niveau (`.call()`, etc.). ```solidity function testReadWriteCached() public { uint cacheGoat = worm.cacheWrite(0x60A7); ``` -Ici, nous utilisons le fait que `cacheWrite` renvoie la clé du cache. Ce n'est pas quelque chose que nous prévoyons d'utiliser en production, car `cacheWrite` modifie l'état, et ne peut donc être appelé que lors d'une transaction. Les transactions n'ont pas de valeurs de retour, si elles ont des résultats, ces résultats sont censés être émis sous forme d'événements. Ainsi, la valeur de retour de `cacheWrite` n'est accessible qu'à partir du code sur la chaîne, et le code sur la chaîne n'a pas besoin de mise en cache des paramètres. +Ici, nous utilisons le fait que `cacheWrite` retourne la clé du cache. Ce n'est pas quelque chose que nous nous attendrions à utiliser en production, car `cacheWrite` modifie l'état, et ne peut donc être appelé que lors d'une transaction. Les transactions n'ont pas de valeurs de retour ; si elles ont des résultats, ces derniers sont censés être émis sous forme d'événements. La valeur de retour de `cacheWrite` n'est donc accessible qu'à partir du code en chaîne, et le code en chaîne n'a pas besoin de mise en cache des paramètres. ```solidity (_success,) = address(worm).call(_callInput); ``` -C'est ainsi que nous indiquons à Solidity que, bien que `.call()` ait deux valeurs de retour, nous ne nous préoccupons que de la première. +C'est ainsi que nous indiquons à Solidity que, bien que `.call()` ait deux valeurs de retour, nous ne nous intéressons qu'à la première. ```solidity (_success,) = address(worm).call(_callInput); assertEq(_success, false); ``` -Puisque nous utilisons la fonction de bas niveau `
.call()`, nous ne pouvons pas utiliser `vm.expectRevert()` et devons regarder la valeur de résultat booléen que nous obtenons de l'appel. +Puisque nous utilisons la fonction de bas niveau `
.call()`, nous ne pouvons pas utiliser `vm.expectRevert()` et devons regarder la valeur de succès booléenne que nous obtenons de l'appel. ```solidity event EntryWritten(uint indexed key, uint indexed value); @@ -756,21 +752,21 @@ Puisque nous utilisons la fonction de bas niveau `
.call()`, nous ne pou (_success,) = address(worm).call(_callInput); ``` -C'est ainsi que nous vérifions que le code [émet correctement un événement](https://book.getfoundry.sh/cheatcodes/expect-emit) dans Foundry. +C'est la manière de vérifier que le code [émet un événement correctement](https://getfoundry.sh/reference/cheatcodes/expect-emit/) dans Foundry. ### Le client {#the-client} -Une chose que vous n'obtenez pas avec les tests Solidity, c'est du code JavaScript que vous pouvez couper et coller dans votre propre application. Pour écrire ce code, j'ai déployé WORM sur [Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli), le nouveau réseau de test d'[Optimism](https://www.optimism.io/). Il se trouve à l'adresse [`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a). +Une chose que vous n'obtenez pas avec les tests Solidity, c'est du code JavaScript que vous pouvez copier et coller dans votre propre application. Pour écrire ce code, j'ai déployé WORM sur [Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli), le nouveau réseau de test d'[Optimism](https://www.optimism.io/). Il se trouve à l'adresse [`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a). -[Vous pouvez voir le code JavaScript pour le client ici](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js). Pour l'utiliser : +[Vous pouvez voir le code JavaScript pour le client ici](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js). Pour l'utiliser : -1. Clonez le dépôt git : +1. Clonez le dépôt git : ```sh git clone https://github.com/qbzzt/20220915-all-you-can-cache.git ``` -2. Installez les packages nécessaires : +2. Installez les paquets nécessaires : ```sh cd javascript @@ -785,10 +781,10 @@ Une chose que vous n'obtenez pas avec les tests Solidity, c'est du code JavaScri 4. Modifiez `.env` selon votre configuration : - | Paramètre | Valeur | - | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | MNEMONIC | La mnémonique d'un compte qui dispose de suffisamment d'ETH pour payer une transaction. [Vous pouvez obtenir de l'ETH gratuit pour le réseau Optimism Goerli ici](https://optimismfaucet.xyz/). | - | OPTIMISM_GOERLI_URL | URL vers Optimism Goerli. Le point de terminaison public, `https://goerli.optimism.io`, est à débit limité mais suffisant pour ce dont nous avons besoin ici | + | Paramètre | Valeur | + | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | MNEMONIC | La mnémonique d'un compte qui dispose de suffisamment d'ETH pour payer une transaction. [Vous pouvez obtenir de l'ETH gratuit pour le réseau Optimism Goerli ici](https://optimismfaucet.xyz/). | + | OPTIMISM_GOERLI_URL | URL vers Optimism Goerli. Le point de terminaison public, `https://goerli.optimism.io`, a un débit limité mais suffisant pour ce dont nous avons besoin ici. | 5. Exécutez `index.js`. @@ -796,9 +792,9 @@ Une chose que vous n'obtenez pas avec les tests Solidity, c'est du code JavaScri node index.js ``` - Cet exemple d'application écrit d'abord une entrée dans WORM, affichant les données d'appel et un lien vers la transaction sur Etherscan. Ensuite, elle lit cette entrée, affiche la clé qu'elle utilise et les valeurs de l'entrée (valeur, numéro de bloc et auteur). + Cet exemple d'application écrit d'abord une entrée dans WORM, en affichant les données d'appel et un lien vers la transaction sur Etherscan. Ensuite, il relit cette entrée et affiche la clé qu'il utilise et les valeurs de l'entrée (valeur, numéro de bloc et auteur). -La plupart des clients sont du JavaScript Dapp normal. Donc, encore une fois, nous ne passerons en revue que les parties intéressantes. +La plupart du client est du JavaScript de Dapp normal. Donc, encore une fois, nous ne passerons en revue que les parties intéressantes. ```javascript . @@ -807,20 +803,20 @@ La plupart des clients sont du JavaScript Dapp normal. Donc, encore une fois, no const main = async () => { const func = await worm.WRITE_ENTRY_CACHED() - // Need a new key every time + // Il faut une nouvelle clé à chaque fois const key = await worm.encodeVal(Number(new Date())) ``` -Un emplacement donné ne peut être écrit qu'une seule fois, donc nous utilisons l'horodatage pour nous assurer que nous ne réutilisons pas les emplacements. +Un emplacement donné ne peut être écrit qu'une seule fois, nous utilisons donc l'horodatage pour nous assurer de ne pas réutiliser les emplacements. ```javascript const val = await worm.encodeVal("0x600D") -// Write an entry +// Écrire une entrée const calldata = func + key.slice(2) + val.slice(2) ``` -Ethers s'attend à ce que les données d'appel soient une chaîne hexadécimale, `0x` suivie d'un nombre pair de chiffres hexadécimaux. Comme `key` et `val` commencent tous les deux par `0x`, nous devons supprimer ces en-têtes. +Ethers s'attend à ce que les données d'appel soient une chaîne hexadécimale, `0x` suivi d'un nombre pair de chiffres hexadécimaux. Comme `key` et `val` commencent tous les deux par `0x`, nous devons supprimer ces en-têtes. ```javascript const tx = await worm.populateTransaction.writeEntryCached() @@ -829,14 +825,14 @@ tx.data = calldata sentTx = await wallet.sendTransaction(tx) ``` -Comme pour le code de test Solidity, nous ne pouvons normalement pas appeler une fonction de mise en cache. Au lieu de cela, nous devons utiliser un mécanisme de bas niveau. +Comme pour le code de test Solidity, nous ne pouvons pas appeler une fonction mise en cache normalement. À la place, nous devons utiliser un mécanisme de plus bas niveau. ```javascript . . . - // Read the entry just written - const realKey = '0x' + key.slice(4) // remove the FF flag + // Lire l'entrée qui vient d'être écrite + const realKey = '0x' + key.slice(4) // supprimer l'indicateur FF const entryRead = await worm.readEntry(realKey) . . @@ -847,21 +843,24 @@ Pour lire les entrées, nous pouvons utiliser le mécanisme normal. Il n'est pas ## Conclusion {#conclusion} -Le code dans cet article est une preuve de concept, le but étant de rendre l'idée facile à comprendre. Pour un système exploitable, vous devriez peut-être mettre en œuvre certaines fonctionnalités supplémentaires : +Le code de cet article est une preuve de concept, le but est de rendre l'idée facile à comprendre. Pour un système prêt pour la production, vous pourriez vouloir implémenter des fonctionnalités supplémentaires : -- Gérer les valeurs qui ne sont pas de type `uint256`. Par exemple, les chaines de caractères. -- Au lieu d'un cache global, peut-être avoir une correspondance entre les utilisateurs et les caches. Différents utilisateurs utilisent différentes valeurs. +- Gérer les valeurs qui ne sont pas de type `uint256`. Par exemple, des chaînes de caractères. +- Au lieu d'un cache global, vous pourriez avoir une correspondance entre les utilisateurs et les caches. Différents utilisateurs utilisent des valeurs différentes. - Les valeurs utilisées pour les adresses sont distinctes de celles utilisées à d'autres fins. Il pourrait être judicieux d'avoir un cache séparé uniquement pour les adresses. -- Actuellement, les clés de cache fonctionnent selon un algorithme « premier arrivé, clé la plus petite ». Les seize premières valeurs peuvent être envoyées en un seul octet. Les 4080 valeurs suivantes peuvent être envoyées en deux octets. Le million de valeurs suivant est de trois octets, etc. Un système de production doit conserver les compteurs d'utilisation sur les entrées du cache et les réorganiser de manière à ce que les seize valeurs _les plus courantes_ soient sur un octet, les 4 080 valeurs les plus courantes suivantes sur deux octets, etc. +- Actuellement, les clés de cache suivent un algorithme « premier arrivé, clé la plus petite ». Les seize premières valeurs peuvent être envoyées en un seul octet. Les 4080 valeurs suivantes peuvent être envoyées sur deux octets. Le million de valeurs suivant est de trois octets, etc. Un système de production devrait conserver des compteurs d'utilisation sur les entrées du cache et les réorganiser de manière à ce que les seize valeurs _les plus courantes_ soient sur un octet, les 4 080 valeurs les plus courantes suivantes sur deux octets, etc. Cependant, c'est une opération potentiellement dangereuse. Imaginez la séquence d'événements suivante : - 1. Noam Naïf appelle `encodeVal` pour encoder l'adresse à laquelle il souhaite envoyer des tokens. Cette adresse est l'une des premières utilisées dans l'application, donc la valeur encodée est 0x06. Il s'agit d'une fonction en `lecture`, pas d'une transaction, donc elle concerne uniquement Noam et le nœud qu'il utilise, et personne d'autre n'est au courant + 1. Noam Naïf appelle `encodeVal` pour encoder l'adresse à laquelle il veut envoyer des jetons. Cette adresse est l'une des premières utilisées sur l'application, donc la valeur encodée est 0x06. C'est une fonction `view`, pas une transaction, donc c'est entre Noam et le nœud qu'il utilise, et personne d'autre n'est au courant. - 2. Paulo Proprio exécute l'opération de réorganisation du cache. Très peu de gens utilisent réellement cette adresse, elle est donc maintenant encodée en 0x201122. Une valeur différente, 1018, se voit attribuer 0x06. + 2. Pierre Propriétaire exécute l'opération de réorganisation du cache. Très peu de gens utilisent réellement cette adresse, elle est donc maintenant encodée en 0x201122. Une valeur différente, 1018, se voit attribuer 0x06. - 3. Noam Naïf envoie ses tokens à 0x06. Ils vont à l'adresse `0x0000000000000000000000000de0b6b3a7640000`, et comme personne ne connaît la clé privée de cette adresse, ils restent bloqués là. Noam n'est _pas content_. + 3. Noam Naïf envoie ses jetons à 0x06. Ils vont à l'adresse `0x0000000000000000000000000de0b6b3a7640000`, et comme personne ne connaît la clé privée de cette adresse, ils sont simplement bloqués là. Noam n'est _pas content_. - Il existe des moyens de résoudre ce problème, ainsi que le problème lié aux transactions qui sont dans le mempool pendant la réorganisation du cache, mais vous devez en être conscient. + Il existe des moyens de résoudre ce problème, ainsi que le problème connexe des transactions qui se trouvent dans le mempool pendant la réorganisation du cache, mais vous devez en être conscient. + +J'ai fait ici une démonstration de la mise en cache avec Optimism, parce que je suis un employé d'Optimism et que c'est le rollup que je connais le mieux. Mais cela devrait fonctionner avec n'importe quel rollup qui facture un coût minimal pour le traitement interne, de sorte qu'en comparaison, l'écriture des données de transaction sur la L1 soit la principale dépense. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). -J'ai démontré la mise en cache ici avec Optimism, car je suis un employé d'Optimism et c'est la rollup que je connais le mieux. Mais cela devrait fonctionner avec n'importe quelle rollup qui facture un coût minimal pour le traitement interne, de sorte qu'en comparaison, l'écriture des données de la transaction sur L1 soit le principal coût. diff --git a/public/content/translations/fr/developers/tutorials/app-plasma/index.md b/public/content/translations/fr/developers/tutorials/app-plasma/index.md new file mode 100644 index 00000000000..c28c16f7469 --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/app-plasma/index.md @@ -0,0 +1,1261 @@ +--- +title: "Écrire un plasma spécifique à une application qui préserve la confidentialité" +description: "Dans ce tutoriel, nous créons une banque semi-secrète pour les dépôts. La banque est un composant centralisé ; elle connaît le solde de chaque utilisateur. Cependant, ces informations ne sont pas stockées sur la chaîne. Au lieu de cela, la banque publie un hachage de l'état. Chaque fois qu'une transaction a lieu, la banque publie le nouveau hachage, ainsi qu'une preuve à divulgation nulle de connaissance qu'elle a une transaction signée qui change l'état de hachage au nouveau. Après avoir lu ce tutoriel, vous comprendrez non seulement comment utiliser les preuves à divulgation nulle de connaissance, mais aussi pourquoi vous les utilisez et comment le faire en toute sécurité." +author: Ori Pomerantz +tags: + [ + "preuve à divulgation nulle de connaissance", + "serveur", + "hors-chaîne", + "confidentialité" + ] +skill: advanced +lang: fr +published: 2025-10-15 +--- + +## Introduction {#introduction} + +Contrairement aux [rollups](/developers/docs/scaling/zk-rollups/), les [plasmas](/developers/docs/scaling/plasma) utilisent le réseau principal Ethereum pour l'intégrité, mais pas pour la disponibilité. Dans cet article, nous écrivons une application qui se comporte comme un plasma, avec Ethereum garantissant l'intégrité (pas de changements non autorisés) mais pas la disponibilité (un composant centralisé peut tomber en panne et désactiver tout le système). + +L'application que nous écrivons ici est une banque qui préserve la confidentialité. Différentes adresses ont des comptes avec des soldes, et elles peuvent envoyer de l'argent (ETH) à d'autres comptes. La banque publie les hachages de l'état (comptes et soldes) et des transactions, mais garde les soldes réels hors chaîne où ils peuvent rester privés. + +## Conception {#design} + +Ce n'est pas un système prêt pour la production, mais un outil pédagogique. En tant que tel, il est écrit avec plusieurs hypothèses simplificatrices. + +- Pool de comptes fixe. Il y a un nombre spécifique de comptes, et chaque compte appartient à une adresse prédéterminée. Cela rend le système beaucoup plus simple car il est difficile de gérer des structures de données de taille variable dans les preuves à divulgation nulle de connaissance. Pour un système prêt pour la production, nous pouvons utiliser la [racine de Merkle](/developers/tutorials/merkle-proofs-for-offline-data-integrity/) comme hachage d'état et fournir des preuves de Merkle pour les soldes requis. + +- Stockage en mémoire. Sur un système de production, nous devons écrire tous les soldes des comptes sur le disque pour les préserver en cas de redémarrage. Ici, ce n'est pas grave si les informations sont simplement perdues. + +- Transferts uniquement. Un système de production nécessiterait un moyen de déposer des actifs dans la banque et de les retirer. Mais le but ici est simplement d'illustrer le concept, donc cette banque est limitée aux transferts. + +### Preuves à divulgation nulle de connaissance {#zero-knowledge-proofs} + +À un niveau fondamental, une preuve à divulgation nulle de connaissance montre que le prouveur connaît certaines données, _Donnéesprivées_, de telle sorte qu'il existe une relation _Relation_ entre certaines données publiques, _Donnéespubliques_, et _Donnéesprivées_. Le vérificateur connaît la _Relation_ et les _Donnéespubliques_. + +Pour préserver la confidentialité, nous avons besoin que les états et les transactions soient privés. Mais pour garantir l'intégrité, nous avons besoin que le [hachage cryptographique](https://en.wikipedia.org/wiki/Cryptographic_hash_function) des états soit public. Pour prouver aux personnes qui soumettent des transactions que ces transactions ont bien eu lieu, nous devons également publier les hachages de transaction. + +Dans la plupart des cas, _Donnéesprivées_ est l'entrée du programme de preuve à divulgation nulle de connaissance, et _Donnéespubliques_ est la sortie. + +Ces champs dans _Donnéesprivées_ : + +- _Étatn_, l'ancien état +- _Étatn+1_, le nouvel état +- _Transaction_, une transaction qui passe de l'ancien état au nouveau. Cette transaction doit inclure ces champs : + - _Adresse de destination_ qui reçoit le transfert + - _Montant_ transféré + - _Nonce_ pour garantir que chaque transaction ne puisse être traitée qu'une seule fois. + L'adresse source n'a pas besoin de figurer dans la transaction, car elle peut être récupérée à partir de la signature. +- _Signature_, une signature qui est autorisée à effectuer la transaction. Dans notre cas, la seule adresse autorisée à effectuer une transaction est l'adresse source. Parce que notre système à divulgation nulle de connaissance fonctionne comme il le fait, nous avons également besoin de la clé publique du compte, en plus de la signature Ethereum. + +Voici les champs dans _Donnéespubliques_ : + +- _Hachage(Étatn)_, le hachage de l'ancien état +- _Hachage(Étatn+1)_, le hachage du nouvel état +- _Hachage(Transaction)_, le hachage de la transaction qui fait passer l'état de _Étatn_ à _Étatn+1_. + +La relation vérifie plusieurs conditions : + +- Les hachages publics sont bien les bons hachages pour les champs privés. +- La transaction, lorsqu'elle est appliquée à l'ancien état, aboutit au nouvel état. +- La signature provient de l'adresse source de la transaction. + +En raison des propriétés des fonctions de hachage cryptographique, la preuve de ces conditions suffit à garantir l'intégrité. + +### Structures de données {#data-structures} + +La structure de données principale est l'état détenu par le serveur. Pour chaque compte, le serveur garde une trace du solde du compte et d'un [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce), utilisé pour empêcher les [attaques par rejeu](https://en.wikipedia.org/wiki/Replay_attack). + +### Composants {#components} + +Ce système nécessite deux composants : + +- Le _serveur_ qui reçoit les transactions, les traite et publie les hachages sur la chaîne ainsi que les preuves à divulgation nulle de connaissance. +- Un _contrat intelligent_ qui stocke les hachages et vérifie les preuves à divulgation nulle de connaissance pour s'assurer que les transitions d'état sont légitimes. + +### Flux de données et de contrôle {#flows} + +Ce sont les façons dont les différents composants communiquent pour effectuer un transfert d'un compte à un autre. + +1. Un navigateur web soumet une transaction signée demandant un transfert du compte du signataire vers un autre compte. + +2. Le serveur vérifie que la transaction est valide : + + - Le signataire a un compte à la banque avec un solde suffisant. + - Le destinataire a un compte à la banque. + +3. Le serveur calcule le nouvel état en soustrayant le montant transféré du solde du signataire et en l'ajoutant au solde du destinataire. + +4. Le serveur calcule une preuve à divulgation nulle de connaissance que le changement d'état est valide. + +5. Le serveur soumet à Ethereum une transaction qui inclut : + + - Le nouveau hachage d'état + - Le hachage de la transaction (afin que l'expéditeur de la transaction puisse savoir qu'elle a été traitée) + - La preuve à divulgation nulle de connaissance qui prouve que la transition vers le nouvel état est valide + +6. Le contrat intelligent vérifie la preuve à divulgation nulle de connaissance. + +7. Si la preuve à divulgation nulle de connaissance est validée, le contrat intelligent effectue ces actions : + - Mettre à jour le hachage de l'état actuel avec le nouveau hachage d'état + - Émettre une entrée de journal avec le nouveau hachage d'état et le hachage de la transaction + +### Outils {#tools} + +Pour le code côté client, nous allons utiliser [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) et [Wagmi](https://wagmi.sh/). Ce sont des outils standard de l'industrie ; si vous ne les connaissez pas, vous pouvez utiliser [ce tutoriel](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). + +La majorité du serveur est écrite en JavaScript à l'aide de [Node](https://nodejs.org/en). La partie à divulgation nulle de connaissance est écrite en [Noir](https://noir-lang.org/). Nous avons besoin de la version `1.0.0-beta.10`, donc après avoir [installé Noir comme indiqué](https://noir-lang.org/docs/getting_started/quick_start), exécutez : + +``` +noirup -v 1.0.0-beta.10 +``` + +La blockchain que nous utilisons est `anvil`, une blockchain de test locale qui fait partie de [Foundry](https://getfoundry.sh/introduction/installation). + +## Implémentation {#implementation} + +Comme il s'agit d'un système complexe, nous allons l'implémenter par étapes. + +### Étape 1 - Divulgation nulle de connaissance manuelle {#stage-1} + +Pour la première étape, nous signerons une transaction dans le navigateur, puis fournirons manuellement les informations à la preuve à divulgation nulle de connaissance. Le code à divulgation nulle de connaissance s'attend à recevoir ces informations dans `server/noir/Prover.toml` (documenté [ici](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1)). + +Pour le voir en action : + +1. Assurez-vous que [Node](https://nodejs.org/en/download) et [Noir](https://noir-lang.org/install) sont installés. De préférence, installez-les sur un système UNIX tel que macOS, Linux ou [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). + +2. Téléchargez le code de l'étape 1 et démarrez le serveur web pour servir le code client. + + ```sh + git clone https://github.com/qbzzt/250911-zk-bank.git -b 01-manual-zk + cd 250911-zk-bank + cd client + npm install + npm run dev + ``` + + La raison pour laquelle vous avez besoin d'un serveur web ici est que, pour prévenir certains types de fraude, de nombreux portefeuilles (tels que MetaMask) n'acceptent pas les fichiers servis directement depuis le disque + +3. Ouvrez un navigateur avec un portefeuille. + +4. Dans le portefeuille, saisissez une nouvelle phrase secrète. Notez que cela supprimera votre phrase secrète existante, donc _assurez-vous d'avoir une sauvegarde_. + + La phrase secrète est `test test test test test test test test test test test junk`, la phrase secrète de test par défaut pour anvil. + +5. Accédez au [code côté client](http://localhost:5173/). + +6. Connectez-vous au portefeuille et sélectionnez votre compte de destination et le montant. + +7. Cliquez sur **Signer** et signez la transaction. + +8. Sous l'en-tête **Prover.toml**, vous trouverez du texte. Remplacez `server/noir/Prover.toml` par ce texte. + +9. Exécutez la preuve à divulgation nulle de connaissance. + + ```sh + cd ../server/noir + nargo execute + ``` + + La sortie devrait être similaire à + + ``` + ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute + + [zkBank] Témoin de circuit résolu avec succès + [zkBank] Témoin enregistré dans target/zkBank.gz + [zkBank] Sortie du circuit : (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85) + ``` + +10. Comparez les deux dernières valeurs au hachage que vous voyez sur le navigateur web pour voir si le message est correctement haché. + +#### `server/noir/Prover.toml` {#server-noir-prover-toml} + +[Ce fichier](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) montre le format d'information attendu par Noir. + +```toml +message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0 " +``` + +Le message est au format texte, ce qui le rend facile à comprendre pour l'utilisateur (ce qui est nécessaire lors de la signature) et à analyser pour le code Noir. Le montant est exprimé en finneys pour permettre des transferts fractionnés d'une part, et être facilement lisible d'autre part. Le dernier nombre est le [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). + +La chaîne de caractères fait 100 caractères de long. Les preuves à divulgation nulle de connaissance ne gèrent pas bien les données de taille variable, il est donc souvent nécessaire de compléter les données. + +```toml +pubKeyX=["0x83",...,"0x75"] +pubKeyY=["0x35",...,"0xa5"] +signature=["0xb1",...,"0x0d"] +``` + +Ces trois paramètres sont des tableaux d'octets de taille fixe. + +```toml +[[accounts]] +address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance=100_000 +nonce=0 + +[[accounts]] +address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +balance=100_000 +nonce=0 +``` + +C'est la façon de spécifier un tableau de structures. Pour chaque entrée, nous spécifions l'adresse, le solde (en milliETH alias [finney](https://cryptovalleyjournal.com/glossary/finney/)), et la valeur de nonce suivante. + +#### `client/src/Transfer.tsx` {#client-src-transfer-tsx} + +[Ce fichier](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx) met en œuvre le traitement côté client et génère le fichier `server/noir/Prover.toml` (celui qui inclut les paramètres de la preuve à divulgation nulle de connaissance). + +Voici l'explication des parties les plus intéressantes. + +```tsx +export default attrs => { +``` + +Cette fonction crée le composant React `Transfer`, que d'autres fichiers peuvent importer. + +```tsx + const accounts = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + ] +``` + +Ce sont les adresses de compte, les adresses créées par le `test... phrase secrète `test junk`. Si vous voulez utiliser vos propres adresses, il suffit de modifier cette définition. + +```tsx + const account = useAccount() + const wallet = createWalletClient({ + transport: custom(window.ethereum!) + }) +``` + +Ces [hooks Wagmi](https://wagmi.sh/react/api/hooks) nous permettent d'accéder à la bibliothèque [viem](https://viem.sh/) et au portefeuille. + +```tsx + const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ") +``` + +C'est le message, complété par des espaces. Chaque fois qu'une des variables [`useState`](https://react.dev/reference/react/useState) change, le composant est redessiné et `message` est mis à jour. + +```tsx + const sign = async () => { +``` + +Cette fonction est appelée lorsque l'utilisateur clique sur le bouton **Signer**. Le message est automatiquement mis à jour, mais la signature nécessite l'approbation de l'utilisateur dans le portefeuille, et nous ne voulons pas la demander sauf si nécessaire. + +```tsx + const signature = await wallet.signMessage({ + account: fromAccount, + message, + }) +``` + +Demander au portefeuille de [signer le message](https://viem.sh/docs/accounts/local/signMessage). + +```tsx + const hash = hashMessage(message) +``` + +Obtenir le hachage du message. Il est utile de le fournir à l'utilisateur pour le débogage (du code Noir). + +```tsx + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +[Obtenir la clé publique](https://viem.sh/docs/utilities/recoverPublicKey). Ceci est requis pour la fonction Noir [`ecrecover`](https://github.com/colinnielsen/ecrecover-noir). + +```tsx + setSignature(signature) + setHash(hash) + setPubKey(pubKey) +``` + +Définir les variables d'état. Cela redessine le composant (après la fin de la fonction `sign`) et montre à l'utilisateur les valeurs mises à jour. + +```tsx + let proverToml = ` +``` + +Le texte pour `Prover.toml`. + +```tsx +message="${message}" + +pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))} +pubKeyY=${hexToArray(pubKey.slice(4+2*32))} +``` + +Viem nous fournit la clé publique sous la forme d'une chaîne hexadécimale de 65 octets. Le premier octet est `0x04`, un marqueur de version. Il est suivi de 32 octets pour le `x` de la clé publique, puis de 32 octets pour le `y` de la clé publique. + +Cependant, Noir s'attend à recevoir cette information sous forme de deux tableaux d'octets, un pour `x` et un pour `y`. Il est plus facile de l'analyser ici, côté client, plutôt que dans le cadre de la preuve à divulgation nulle de connaissance. + +Notez que c'est une bonne pratique en matière de preuve à divulgation nulle de connaissance en général. Le code à l'intérieur d'une preuve à divulgation nulle de connaissance est coûteux, donc tout traitement qui peut être effectué en dehors de la preuve à divulgation nulle de connaissance _devrait_ être effectué en dehors de la preuve à divulgation nulle de connaissance. + +```tsx +signature=${hexToArray(signature.slice(2,-2))} +``` + +La signature est également fournie sous la forme d'une chaîne hexadécimale de 65 octets. Cependant, le dernier octet n'est nécessaire que pour récupérer la clé publique. Comme la clé publique sera déjà fournie au code Noir, nous n'en avons pas besoin pour vérifier la signature, et le code Noir ne l'exige pas. + +```tsx +${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")} +` +``` + +Fournir les comptes. + +```tsx + setProverToml(proverToml) + } + + return ( + <> +

Transfer

+``` + +Ceci est le format HTML (plus précisément, [JSX](https://react.dev/learn/writing-markup-with-jsx)) du composant. + +#### `server/noir/src/main.nr` {#server-noir-src-main-nr} + +[Ce fichier](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr) est le code réel de la preuve à divulgation nulle de connaissance. + +``` +use std::hash::pedersen_hash; +``` + +Le [hachage de Pedersen](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/) est fourni avec la [bibliothèque standard de Noir](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash). Les preuves à divulgation nulle de connaissance utilisent couramment cette fonction de hachage. Il est beaucoup plus facile à calculer à l'intérieur de [circuits arithmétiques](https://rareskills.io/post/arithmetic-circuit) par rapport aux fonctions de hachage standard. + +``` +use keccak256::keccak256; +use dep::ecrecover; +``` + +Ces deux fonctions sont des bibliothèques externes, définies dans [`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml). Elles sont précisément ce pour quoi elles sont nommées, une fonction qui calcule le [hachage keccak256](https://emn178.github.io/online-tools/keccak_256.html) et une fonction qui vérifie les signatures Ethereum et récupère l'adresse Ethereum du signataire. + +``` +global ACCOUNT_NUMBER : u32 = 5; +``` + +Noir est inspiré de [Rust](https://www.rust-lang.org/). Les variables, par défaut, sont des constantes. C'est ainsi que nous définissons les constantes de configuration globales. Plus précisément, `ACCOUNT_NUMBER` est le nombre de comptes que nous stockons. + +Les types de données nommés `u` sont ce nombre de bits, non signés. Les seuls types pris en charge sont `u8`, `u16`, `u32`, `u64` et `u128`. + +``` +global FLAT_ACCOUNT_FIELDS : u32 = 2; +``` + +Cette variable est utilisée pour le hachage de Pedersen des comptes, comme expliqué ci-dessous. + +``` +global MESSAGE_LENGTH : u32 = 100; +``` + +Comme expliqué ci-dessus, la longueur du message est fixe. Elle est spécifiée ici. + +``` +global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30]; +global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH; +``` + +Les [signatures EIP-191](https://eips.ethereum.org/EIPS/eip-191) nécessitent un tampon avec un préfixe de 26 octets, suivi de la longueur du message en ASCII, et enfin du message lui-même. + +``` +struct Account { + balance: u128, + address: Field, + nonce: u32, +} +``` + +Les informations que nous stockons sur un compte. [`Field`](https://noir-lang.org/docs/noir/concepts/data_types/fields) est un nombre, généralement jusqu'à 253 bits, qui peut être utilisé directement dans le [circuit arithmétique](https://rareskills.io/post/arithmetic-circuit) qui met en œuvre la preuve à divulgation nulle de connaissance. Ici, nous utilisons le `Field` pour stocker une adresse Ethereum de 160 bits. + +``` +struct TransferTxn { + from: Field, + to: Field, + amount: u128, + nonce: u32 +} +``` + +Les informations que nous stockons pour une transaction de transfert. + +``` +fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] { +``` + +Une définition de fonction. Le paramètre est une information `Account`. Le résultat est un tableau de variables `Field`, dont la longueur est `FLAT_ACCOUNT_FIELDS` + +``` + let flat = [ + account.address, + ((account.balance << 32) + account.nonce.into()).into(), + ]; +``` + +La première valeur dans le tableau est l'adresse du compte. La seconde inclut à la fois le solde et le nonce. Les appels `.into()` changent un nombre vers le type de données dont il a besoin. `account.nonce` est une valeur `u32`, mais pour l'ajouter à `account.balance « 32`, une valeur `u128`, elle doit être un `u128`. C'est le premier `.into()`. Le second convertit le résultat `u128` en un `Field` pour qu'il s'insère dans le tableau. + +``` + flat +} +``` + +Dans Noir, les fonctions ne peuvent retourner une valeur qu'à la fin (il n'y a pas de retour anticipé). Pour spécifier la valeur de retour, vous l'évaluez juste avant le crochet de fermeture de la fonction. + +``` +fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] { +``` + +Cette fonction transforme le tableau de comptes en un tableau `Field`, qui peut être utilisé comme entrée pour un hachage de Petersen. + +``` + let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER]; +``` + +C'est ainsi que vous spécifiez une variable mutable, c'est-à-dire, _pas_ une constante. Les variables dans Noir doivent toujours avoir une valeur, donc nous initialisons cette variable à tous zéros. + +``` + for i in 0..ACCOUNT_NUMBER { +``` + +Ceci est une boucle `for`. Notez que les limites sont des constantes. Les boucles Noir doivent avoir leurs limites connues au moment de la compilation. La raison est que les circuits arithmétiques ne prennent pas en charge le contrôle de flux. Lors du traitement d'une boucle `for`, le compilateur place simplement le code qu'elle contient plusieurs fois, une pour chaque itération. + +``` + let fields = flatten_account(accounts[i]); + for j in 0..FLAT_ACCOUNT_FIELDS { + flat[i*FLAT_ACCOUNT_FIELDS + j] = fields[j]; + } + } + + flat +} + +fn hash_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> Field { + pedersen_hash(flatten_accounts(accounts)) +} +``` + +Enfin, nous arrivons à la fonction qui hache le tableau des comptes. + +``` +fn find_account(accounts: [Account; ACCOUNT_NUMBER], address: Field) -> u32 { + let mut account : u32 = ACCOUNT_NUMBER; + + for i in 0..ACCOUNT_NUMBER { + if accounts[i].address == address { + account = i; + } + } +``` + +Cette fonction trouve le compte avec une adresse spécifique. Cette fonction serait terriblement inefficace dans un code standard car elle itère sur tous les comptes, même après avoir trouvé l'adresse. + +Cependant, dans les preuves à divulgation nulle de connaissance, il n'y a pas de contrôle de flux. Si nous avons besoin de vérifier une condition, nous devons la vérifier à chaque fois. + +Une chose similaire se produit avec les instructions `if`. L'instruction `if` dans la boucle ci-dessus est traduite en ces énoncés mathématiques. + +_conditionrésultat = accounts[i].address == address_ // un s'ils sont égaux, zéro sinon + +_comptenouveau = conditionrésultat\*i + (1-conditionrésultat)\*compteancien_ + +```rust + assert (account < ACCOUNT_NUMBER, f"{address} n'a pas de compte"); + + account +} +``` + +La fonction [`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert) provoque le plantage de la preuve à divulgation nulle de connaissance si l'assertion est fausse. Dans ce cas, si nous ne pouvons pas trouver un compte avec l'adresse pertinente. Pour signaler l'adresse, nous utilisons une [chaîne de format](https://noir-lang.org/docs/noir/concepts/data_types/strings#format-strings). + +```rust +fn apply_transfer_txn(accounts: [Account; ACCOUNT_NUMBER], txn: TransferTxn) -> [Account; ACCOUNT_NUMBER] { +``` + +Cette fonction applique une transaction de transfert et renvoie le nouveau tableau de comptes. + +```rust + let from = find_account(accounts, txn.from); + let to = find_account(accounts, txn.to); + + let (txnFrom, txnAmount, txnNonce, accountNonce) = + (txn.from, txn.amount, txn.nonce, accounts[from].nonce); +``` + +Nous ne pouvons pas accéder aux éléments de structure à l'intérieur d'une chaîne de format dans Noir, nous créons donc une copie utilisable. + +```rust + assert (accounts[from].balance >= txn.amount, + f"{txnFrom} n'a pas {txnAmount} finney"); + + assert (accounts[from].nonce == txn.nonce, + f"La transaction a le nonce {txnNonce}, mais le compte est censé utiliser {accountNonce}"); +``` + +Ce sont deux conditions qui pourraient rendre une transaction invalide. + +```rust + let mut newAccounts = accounts; + + newAccounts[from].balance -= txn.amount; + newAccounts[from].nonce += 1; + newAccounts[to].balance += txn.amount; + + newAccounts +} +``` + +Créez le nouveau tableau de comptes, puis renvoyez-le. + +```rust +fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field +``` + +Cette fonction lit l'adresse du message. + +```rust +{ + let mut result : Field = 0; + + for i in 7..47 { +``` + +L'adresse est toujours longue de 20 octets (alias 40 chiffres hexadécimaux), et commence au caractère #7. + +```rust + result *= 0x10; + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + result += (messageBytes[i]-48).into(); + } + if messageBytes[i] >= 65 & messageBytes[i] <= 70 { // A-F + result += (messageBytes[i]-65+10).into() + } + if messageBytes[i] >= 97 & messageBytes[i] <= 102 { // a-f + result += (messageBytes[i]-97+10).into() + } + } + + result +} + +fn readAmountAndNonce(messageBytes: [u8; MESSAGE_LENGTH]) -> (u128, u32) +``` + +Lire le montant et le nonce du message. + +```rust +{ + let mut amount : u128 = 0; + let mut nonce: u32 = 0; + let mut stillReadingAmount: bool = true; + let mut lookingForNonce: bool = false; + let mut stillReadingNonce: bool = false; +``` + +Dans le message, le premier nombre après l'adresse est le montant de finney (alias millième d'un ETH) à transférer. Le deuxième nombre est le nonce. Tout texte entre eux est ignoré. + +```rust + for i in 48..MESSAGE_LENGTH { + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + let digit = (messageBytes[i]-48); + + if stillReadingAmount { + amount = amount*10 + digit.into(); + } + + if lookingForNonce { // On vient de le trouver + stillReadingNonce = true; + lookingForNonce = false; + } + + if stillReadingNonce { + nonce = nonce*10 + digit.into(); + } + } else { + if stillReadingAmount { + stillReadingAmount = false; + lookingForNonce = true; + } + if stillReadingNonce { + stillReadingNonce = false; + } + } + } + + (amount, nonce) +} +``` + +Le renvoi d'un [tuple](https://noir-lang.org/docs/noir/concepts/data_types/tuples) est la manière Noir de renvoyer plusieurs valeurs d'une fonction. + +```rust +fn readTransferTxn(message: str) -> TransferTxn +{ + let mut txn: TransferTxn = TransferTxn { from: 0, to: 0, amount:0, nonce:0 }; + let messageBytes = message.as_bytes(); + + txn.to = readAddress(messageBytes); + let (amount, nonce) = readAmountAndNonce(messageBytes); + txn.amount = amount; + txn.nonce = nonce; + + txn +} +``` + +Cette fonction convertit le message en octets, puis convertit les montants en un `TransferTxn`. + +```rust +// L'équivalent de hashMessage de Viem +// https://viem.sh/docs/utilities/hashMessage#hashmessage +fn hashMessage(message: str) -> [u8;32] { +``` + +Nous avons pu utiliser le hachage de Pedersen pour les comptes car ils ne sont hachés qu'à l'intérieur de la preuve à divulgation nulle de connaissance. Cependant, dans ce code, nous devons vérifier la signature du message, qui est générée par le navigateur. Pour cela, nous devons suivre le format de signature Ethereum dans [l'EIP 191](https://eips.ethereum.org/EIPS/eip-191). Cela signifie que nous devons créer un tampon combiné avec un préfixe standard, la longueur du message en ASCII et le message lui-même, et utiliser le keccak256 standard d'Ethereum pour le hacher. + +```rust + // Préfixe ASCII + let prefix_bytes = [ + 0x19, // \x19 + 0x45, // 'E' + 0x74, // 't' + 0x68, // 'h' + 0x65, // 'e' + 0x72, // 'r' + 0x65, // 'e' + 0x75, // 'u' + 0x6D, // 'm' + 0x20, // ' ' + 0x53, // 'S' + 0x69, // 'i' + 0x67, // 'g' + 0x6E, // 'n' + 0x65, // 'e' + 0x64, // 'd' + 0x20, // ' ' + 0x4D, // 'M' + 0x65, // 'e' + 0x73, // 's' + 0x73, // 's' + 0x61, // 'a' + 0x67, // 'g' + 0x65, // 'e' + 0x3A, // ':' + 0x0A // '\n' + ]; +``` + +Pour éviter les cas où une application demande à l'utilisateur de signer un message qui peut être utilisé comme une transaction ou à d'autres fins, l'EIP 191 spécifie que tous les messages signés commencent par le caractère 0x19 (qui n'est pas un caractère ASCII valide) suivi de `Ethereum Signed Message:` et d'un saut de ligne. + +```rust + let mut buffer: [u8; HASH_BUFFER_SIZE] = [0u8; HASH_BUFFER_SIZE]; + for i in 0..26 { + buffer[i] = prefix_bytes[i]; + } + + let messageBytes : [u8; MESSAGE_LENGTH] = message.as_bytes(); + + if MESSAGE_LENGTH <= 9 { + for i in 0..1 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+1] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 10 & MESSAGE_LENGTH <= 99 { + for i in 0..2 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+2] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 100 { + for i in 0..3 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+3] = messageBytes[i]; + } + } + + assert(MESSAGE_LENGTH < 1000, "Les messages dont la longueur est supérieure à trois chiffres ne sont pas pris en charge"); +``` + +Gérer les longueurs de message jusqu'à 999 et échouer si c'est plus. J'ai ajouté ce code, même si la longueur du message est une constante, car cela facilite sa modification. Sur un système de production, vous supposeriez probablement que `MESSAGE_LENGTH` ne change pas pour une meilleure performance. + +```rust + keccak256::keccak256(buffer, HASH_BUFFER_SIZE) +} +``` + +Utiliser la fonction standard d'Ethereum `keccak256`. + +```rust +fn signatureToAddressAndHash( + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64] + ) -> (Field, Field, Field) // adresse, 16 premiers octets du hachage, 16 derniers octets du hachage +{ +``` + +Cette fonction vérifie la signature, ce qui nécessite le hachage du message. Elle nous fournit ensuite l'adresse qui l'a signé et le hachage du message. Le hachage du message est fourni sous forme de deux valeurs `Field` car elles sont plus faciles à utiliser dans le reste du programme qu'un tableau d'octets. + +Nous devons utiliser deux valeurs `Field` car les calculs de champ sont effectués [modulo](https://en.wikipedia.org/wiki/Modulo) un grand nombre, mais ce nombre est généralement inférieur à 256 bits (sinon il serait difficile d'effectuer ces calculs dans l'EVM). + +```rust + let hash = hashMessage(message); + + let mut (hash1, hash2) = (0,0); + + for i in 0..16 { + hash1 = hash1*256 + hash[31-i].into(); + hash2 = hash2*256 + hash[15-i].into(); + } +``` + +Spécifiez `hash1` et `hash2` comme des variables mutables, et écrivez le hachage dedans octet par octet. + +```rust + ( + ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), +``` + +Ceci est similaire à la fonction `ecrecover` de [Solidity](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions), avec deux différences importantes : + +- Si la signature n'est pas valide, l'appel échoue sur un `assert` et le programme est interrompu. +- Bien que la clé publique puisse être récupérée à partir de la signature et du hachage, il s'agit d'un traitement qui peut être effectué en externe et qui, par conséquent, ne vaut pas la peine d'être fait à l'intérieur de la preuve à divulgation nulle de connaissance. Si quelqu'un essaie de nous tromper ici, la vérification de la signature échouera. + +```rust + hash1, + hash2 + ) +} + +fn main( + accounts: [Account; ACCOUNT_NUMBER], + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64], + ) -> pub ( + Field, // Hachage de l'ancien tableau de comptes + Field, // Hachage du nouveau tableau de comptes + Field, // 16 premiers octets du hachage du message + Field, // 16 derniers octets du hachage du message + ) +``` + +Enfin, nous atteignons la fonction `main`. Nous devons prouver que nous avons une transaction qui change valablement le hachage des comptes de l'ancienne valeur à la nouvelle. Nous devons également prouver qu'il a ce hachage de transaction spécifique afin que la personne qui l'a envoyé sache que sa transaction a été traitée. + +```rust +{ + let mut txn = readTransferTxn(message); +``` + +Nous avons besoin que `txn` soit mutable car nous ne lisons pas l'adresse d'origine du message, nous la lisons à partir de la signature. + +```rust + let (fromAddress, txnHash1, txnHash2) = signatureToAddressAndHash( + message, + pubKeyX, + pubKeyY, + signature); + + txn.from = fromAddress; + + let newAccounts = apply_transfer_txn(accounts, txn); + + ( + hash_accounts(accounts), + hash_accounts(newAccounts), + txnHash1, + txnHash2 + ) +} +``` + +### Étape 2 - Ajout d'un serveur {#stage-2} + +Dans la deuxième étape, nous ajoutons un serveur qui reçoit et met en œuvre les transactions de transfert du navigateur. + +Pour le voir en action : + +1. Arrêtez Vite s'il est en cours d'exécution. + +2. Téléchargez la branche qui inclut le serveur et assurez-vous que vous avez tous les modules nécessaires. + + ```sh + git checkout 02-add-server + cd client + npm install + cd ../server + npm install + ``` + + Il n'est pas nécessaire de compiler le code Noir, c'est le même code que vous avez utilisé pour l'étape 1. + +3. Démarrer le serveur. + + ```sh + npm run start + ``` + +4. Dans une fenêtre de ligne de commande distincte, exécutez Vite pour servir le code du navigateur. + + ```sh + cd client + npm run dev + ``` + +5. Accédez au code client sur [http://localhost:5173](http://localhost:5173) + +6. Avant de pouvoir émettre une transaction, vous devez connaître le nonce, ainsi que le montant que vous pouvez envoyer. Pour obtenir ces informations, cliquez sur **Mettre à jour les données du compte** et signez le message. + + Nous avons un dilemme ici. D'un côté, nous ne voulons pas signer un message qui peut être réutilisé (une [attaque par rejeu](https://en.wikipedia.org/wiki/Replay_attack)), c'est pourquoi nous voulons un nonce en premier lieu. Cependant, nous n'avons pas encore de nonce. La solution est de choisir un nonce qui ne peut être utilisé qu'une seule fois et que nous avons déjà des deux côtés, comme l'heure actuelle. + + Le problème avec cette solution est que l'heure pourrait ne pas être parfaitement synchronisée. Donc, à la place, nous signons une valeur qui change chaque minute. Cela signifie que notre fenêtre de vulnérabilité aux attaques par rejeu est d'au plus une minute. Considérant qu'en production la requête signée sera protégée par TLS, et que l'autre côté du tunnel - le serveur - peut déjà divulguer le solde et le nonce (il doit les connaître pour fonctionner), c'est un risque acceptable. + +7. Une fois que le navigateur a récupéré le solde et le nonce, il affiche le formulaire de transfert. Sélectionnez l'adresse de destination et le montant, puis cliquez sur **Transférer**. Signez cette demande. + +8. Pour voir le transfert, soit **Mettez à jour les données du compte**, soit regardez dans la fenêtre où vous exécutez le serveur. Le serveur enregistre l'état à chaque fois qu'il change. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Écoute sur le port 3000 + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 traitée + Nouvel état : + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 a 64000 (1) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 a 100000 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC a 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 a 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 a 100000 (0) + Txn send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 traitée + Nouvel état : + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 a 56800 (2) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 a 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC a 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 a 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 a 100000 (0) + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 traitée + Nouvel état : + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 a 53800 (3) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 a 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC a 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 a 139000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 a 100000 (0) + ``` + +#### `server/index.mjs` {#server-index-mjs-1} + +[Ce fichier](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs) contient le processus serveur, et interagit avec le code Noir à [`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr). Voici une explication des parties intéressantes. + +```js +import { Noir } from '@noir-lang/noir_js' +``` + +La bibliothèque [noir.js](https://www.npmjs.com/package/@noir-lang/noir_js) fait l'interface entre le code JavaScript et le code Noir. + +```js +const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json")) +const noir = new Noir(circuit) +``` + +Chargez le circuit arithmétique - le programme Noir compilé que nous avons créé à l'étape précédente - et préparez-vous à l'exécuter. + +```js +// Nous ne fournissons des informations sur le compte qu'en réponse à une demande signée +const accountInformation = async signature => { + const fromAddress = await recoverAddress({ + hash: hashMessage("Get account data " + Math.floor((new Date().getTime())/60000)), + signature + }) +``` + +Pour fournir des informations sur le compte, nous n'avons besoin que de la signature. La raison est que nous savons déjà quel sera le message, et donc le hachage du message. + +```js +const processMessage = async (message, signature) => { +``` + +Traitez un message et exécutez la transaction qu'il encode. + +```js + // Obtenir la clé publique + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +Maintenant que nous exécutons JavaScript sur le serveur, nous pouvons récupérer la clé publique là-bas plutôt que sur le client. + +```js + let noirResult + try { + noirResult = await noir.execute({ + message, + signature: signature.slice(2,-2).match(/.{2}/g).map(x => `0x${x}`), + pubKeyX, + pubKeyY, + accounts: Accounts + }) +``` + +`noir.execute` exécute le programme Noir. Les paramètres sont équivalents à ceux fournis dans [`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml). Notez que les valeurs longues sont fournies sous forme de tableau de chaînes hexadécimales (`["0x60", "0xA7"]`), et non sous forme de valeur hexadécimale unique (`0x60A7`), comme le fait Viem. + +```js + } catch (err) { + console.log(`Erreur Noir : ${err}`) + throw Error("Transaction invalide, non traitée") + } +``` + +S'il y a une erreur, l'attraper et relayer une version simplifiée au client. + +```js + Accounts[fromAccountNumber].nonce++ + Accounts[fromAccountNumber].balance -= amount + Accounts[toAccountNumber].balance += amount +``` + +Appliquez la transaction. Nous l'avons déjà fait dans le code Noir, mais il est plus facile de le faire à nouveau ici plutôt que d'en extraire le résultat. + +```js +let Accounts = [ + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + balance: 5000, + nonce: 0, + }, +``` + +La structure `Accounts` initiale. + +### Étape 3 - Contrats intelligents Ethereum {#stage-3} + +1. Arrêtez les processus du serveur et du client. + +2. Téléchargez la branche avec les contrats intelligents et assurez-vous que vous avez tous les modules nécessaires. + + ```sh + git checkout 03-smart-contracts + cd client + npm install + cd ../server + npm install + ``` + +3. Exécutez `anvil` dans une fenêtre de ligne de commande séparée. + +4. Générez la clé de vérification et le vérificateur de solidité, puis copiez le code du vérificateur dans le projet Solidity. + + ```sh + cd noir + bb write_vk -b ./target/zkBank.json -o ./target --oracle_hash keccak + bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol + cp target/Verifier.sol ../../smart-contracts/src + ``` + +5. Allez dans les contrats intelligents et définissez les variables d'environnement pour utiliser la blockchain `anvil`. + + ```sh + cd ../../smart-contracts + export ETH_RPC_URL=http://localhost:8545 + ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ``` + +6. Déployez `Verifier.sol` et stockez l'adresse dans une variable d'environnement. + + ```sh + VERIFIER_ADDRESS=`forge create src/Verifier.sol:HonkVerifier --private-key $ETH_PRIVATE_KEY --optimize --broadcast | awk '/Deployed to:/ {print $3}'` + echo $VERIFIER_ADDRESS + ``` + +7. Déployez le contrat `ZkBank`. + + ```sh + ZKBANK_ADDRESS=`forge create ZkBank --private-key $ETH_PRIVATE_KEY --broadcast --constructor-args $VERIFIER_ADDRESS 0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b | awk '/Deployed to:/ {print $3}'` + echo $ZKBANK_ADDRESS + ``` + + La valeur `0x199..67b` est le hachage de Pederson de l'état initial de `Comptes`. Si vous modifiez cet état initial dans `server/index.mjs`, vous pouvez exécuter une transaction pour voir le hachage initial rapporté par la preuve à divulgation nulle de connaissance. + +8. Lancez le serveur. + + ```sh + cd ../server + npm run start + ``` + +9. Exécutez le client dans une autre fenêtre de ligne de commande. + + ```sh + cd client + npm run dev + ``` + +10. Exécutez quelques transactions. + +11. Pour vérifier que l'état a changé sur la chaîne, redémarrez le processus du serveur. Voyez que `ZkBank` n'accepte plus les transactions, car la valeur de hachage originale dans les transactions diffère de la valeur de hachage stockée sur la chaîne. + + C'est le type d'erreur attendu. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Écoute sur le port 3000 + Erreur de vérification : ContractFunctionExecutionError : La fonction de contrat "processTransaction" a été annulée pour la raison suivante : + Mauvais hachage de l'ancien état + + Appel de contrat : + adresse : 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + fonction : processTransaction(bytes _proof, bytes32[] _publicInputs) + args : (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000 + ``` + +#### `server/index.mjs` {#server-index-mjs-2} + +Les changements dans ce fichier concernent principalement la création de la preuve réelle et sa soumission sur la chaîne. + +```js +import { exec } from 'child_process' +import util from 'util' + +const execPromise = util.promisify(exec) +``` + +Nous devons utiliser [le paquet Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg) pour créer la preuve réelle à envoyer sur la chaîne. Nous pouvons utiliser ce paquet soit en exécutant l'interface de ligne de commande (`bb`) soit en utilisant la [bibliothèque JavaScript, `bb.js`](https://www.npmjs.com/package/@aztec/bb.js). La bibliothèque JavaScript est beaucoup plus lente que l'exécution de code nativement, nous utilisons donc [`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) ici pour utiliser la ligne de commande. + +Notez que si vous décidez d'utiliser `bb.js`, vous devez utiliser une version compatible avec la version de Noir que vous utilisez. Au moment de la rédaction, la version actuelle de Noir (1.0.0-beta.11) utilise la version 0.87 de `bb.js`. + +```js +const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +``` + +L'adresse ici est celle que vous obtenez lorsque vous commencez avec un `anvil` propre et suivez les instructions ci-dessus. + +```js +const walletClient = createWalletClient({ + chain: anvil, + transport: http(), + account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") +}) +``` + +Cette clé privée est l'un des comptes pré-financés par défaut dans `anvil`. + +```js +const generateProof = async (witness, fileID) => { +``` + +Générer une preuve en utilisant l'exécutable `bb`. + +```js + const fname = `witness-${fileID}.gz` + await fs.writeFile(fname, witness) +``` + +Écrivez le témoin dans un fichier. + +```js + await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`) +``` + +Créez réellement la preuve. Cette étape crée également un fichier avec les variables publiques, mais nous n'en avons pas besoin. Nous avons déjà obtenu ces variables de `noir.execute`. + +```js + const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "") +``` + +La preuve est un tableau JSON de valeurs `Field`, chacune représentée par une valeur hexadécimale. Cependant, nous devons l'envoyer dans la transaction en tant que valeur `bytes` unique, que Viem représente par une grande chaîne hexadécimale. Ici, nous changeons le format en concaténant toutes les valeurs, en supprimant tous les `0x`, puis en en ajoutant un à la fin. + +```js + await execPromise(`rm -r ${fname} ${fileID}`) + + return proof +} +``` + +Nettoyez et retournez la preuve. + +```js +const processMessage = async (message, signature) => { + . + . + . + + const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0")) +``` + +Les champs publics doivent être un tableau de valeurs de 32 octets. Cependant, comme nous devions diviser le hachage de la transaction entre deux valeurs `Field`, il apparaît comme une valeur de 16 octets. Ici, nous ajoutons des zéros pour que Viem comprenne qu'il s'agit bien de 32 octets. + +```js + const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`) +``` + +Chaque adresse n'utilise chaque nonce qu'une seule fois, de sorte que nous pouvons utiliser une combinaison de `fromAddress` et `nonce` comme identifiant unique pour le fichier témoin et le répertoire de sortie. + +```js + try { + await zkBank.write.processTransaction([ + proof, publicFields]) + } catch (err) { + console.log(`Erreur de vérification : ${err}`) + throw Error("Impossible de vérifier la transaction sur la chaîne") + } + . + . + . +} +``` + +Envoyez la transaction à la chaîne. + +#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol} + +Ceci est le code sur la chaîne qui reçoit la transaction. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.21; + +import {HonkVerifier} from "./Verifier.sol"; + +contract ZkBank { + HonkVerifier immutable myVerifier; + bytes32 currentStateHash; + + constructor(address _verifierAddress, bytes32 _initialStateHash) { + currentStateHash = _initialStateHash; + myVerifier = HonkVerifier(_verifierAddress); + } +``` + +Le code sur la chaîne doit garder une trace de deux variables : le vérificateur (un contrat séparé créé par `nargo`) et le hachage de l'état actuel. + +```solidity + event TransactionProcessed( + bytes32 indexed transactionHash, + bytes32 oldStateHash, + bytes32 newStateHash + ); +``` + +Chaque fois que l'état change, nous émettons un événement `TransactionProcessed`. + +```solidity + function processTransaction( + bytes calldata _proof, + bytes32[] calldata _publicFields + ) public { +``` + +Cette fonction traite les transactions. Elle reçoit la preuve (en tant que `bytes`) et les entrées publiques (en tant que tableau `bytes32`), dans le format requis par le vérificateur (pour minimiser le traitement sur la chaîne et donc les coûts de gaz). + +```solidity + require(_publicInputs[0] == currentStateHash, + "Mauvais hachage de l'ancien état"); +``` + +La preuve à divulgation nulle de connaissance doit être que la transaction passe de notre hachage actuel à un nouveau. + +```solidity + myVerifier.verify(_proof, _publicFields); +``` + +Appeler le contrat vérificateur pour vérifier la preuve à divulgation nulle de connaissance. Cette étape annule la transaction si la preuve à divulgation nulle de connaissance est incorrecte. + +```solidity + currentStateHash = _publicFields[1]; + + emit TransactionProcessed( + _publicFields[2]<<128 | _publicFields[3], + _publicFields[0], + _publicFields[1] + ); + } +} +``` + +Si tout est correct, mettez à jour le hachage d'état avec la nouvelle valeur et émettez un événement `TransactionProcessed`. + +## Abus par le composant centralisé {#abuses} + +La sécurité de l'information se compose de trois attributs : + +- _Confidentialité_, les utilisateurs ne peuvent pas lire les informations qu'ils ne sont pas autorisés à lire. +- _Intégrité_, les informations ne peuvent être modifiées que par des utilisateurs autorisés d'une manière autorisée. +- _Disponibilité_, les utilisateurs autorisés peuvent utiliser le système. + +Sur ce système, l'intégrité est assurée par des preuves à divulgation nulle de connaissance. La disponibilité est beaucoup plus difficile à garantir, et la confidentialité est impossible, car la banque doit connaître le solde de chaque compte et toutes les transactions. Il n'y a aucun moyen d'empêcher une entité qui détient des informations de les partager. + +Il serait peut-être possible de créer une banque véritablement confidentielle en utilisant des [adresses furtives](https://vitalik.eth.limo/general/2023/01/20/stealth.html), mais cela dépasse le cadre de cet article. + +### Fausses informations {#false-info} + +Une façon pour le serveur de violer l'intégrité est de fournir de fausses informations lorsque [des données sont demandées](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291). + +Pour résoudre ce problème, nous pouvons écrire un deuxième programme Noir qui reçoit les comptes en tant qu'entrée privée et l'adresse pour laquelle des informations sont demandées en tant qu'entrée publique. La sortie est le solde et le nonce de cette adresse, et le hachage des comptes. + +Bien sûr, cette preuve ne peut pas être vérifiée sur la chaîne, car nous ne voulons pas publier de nonces et de soldes sur la chaîne. Cependant, elle peut être vérifiée par le code client s'exécutant dans le navigateur. + +### Transactions forcées {#forced-txns} + +Le mécanisme habituel pour garantir la disponibilité et empêcher la censure sur les L2 est les [transactions forcées](https://docs.optimism.io/stack/transactions/forced-transaction). Mais les transactions forcées ne se combinent pas avec les preuves à divulgation nulle de connaissance. Le serveur est la seule entité capable de vérifier les transactions. + +Nous pouvons modifier `smart-contracts/src/ZkBank.sol` pour accepter les transactions forcées et empêcher le serveur de changer l'état jusqu'à ce qu'elles soient traitées. Cependant, cela nous expose à une simple attaque par déni de service. Que se passe-t-il si une transaction forcée est invalide et donc impossible à traiter ? + +La solution consiste à avoir une preuve à divulgation nulle de connaissance qu'une transaction forcée est invalide. Cela donne au serveur trois options : + +- Traiter la transaction forcée, en fournissant une preuve à divulgation nulle de connaissance qu'elle a été traitée et le nouveau hachage d'état. +- Rejeter la transaction forcée et fournir au contrat une preuve à divulgation nulle de connaissance que la transaction est invalide (adresse inconnue, mauvais nonce ou solde insuffisant). +- Ignorer la transaction forcée. Il n'y a aucun moyen de forcer le serveur à traiter réellement la transaction, mais cela signifie que l'ensemble du système est indisponible. + +#### Cautionnement de disponibilité {#avail-bonds} + +Dans une implémentation réelle, il y aurait probablement une sorte de motivation de profit pour maintenir le serveur en fonctionnement. Nous pouvons renforcer cette incitation en faisant en sorte que le serveur dépose une caution de disponibilité que n'importe qui peut brûler si une transaction forcée n'est pas traitée dans un certain délai. + +### Mauvais code Noir {#bad-noir-code} + +Normalement, pour que les gens fassent confiance à un contrat intelligent, nous téléchargeons le code source sur un [explorateur de blocs](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract). Cependant, dans le cas des preuves à divulgation nulle de connaissance, cela est insuffisant. + +`Verifier.sol` contient la clé de vérification, qui est une fonction du programme Noir. Cependant, cette clé ne nous dit pas ce qu'était le programme Noir. Pour avoir une solution réellement fiable, vous devez télécharger le programme Noir (et la version qui l'a créé). Sinon, les preuves à divulgation nulle de connaissance pourraient refléter un programme différent, un programme avec une porte dérobée. + +Jusqu'à ce que les explorateurs de blocs nous permettent de télécharger et de vérifier les programmes Noir, vous devriez le faire vous-même (de préférence sur [IPFS](/developers/tutorials/ipfs-decentralized-ui/)). Alors, les utilisateurs avertis pourront télécharger le code source, le compiler eux-mêmes, créer `Verifier.sol`, et vérifier qu'il est identique à celui sur la chaîne. + +## Conclusion {#conclusion} + +Les applications de type Plasma nécessitent un composant centralisé pour le stockage des informations. Cela ouvre des vulnérabilités potentielles mais, en retour, nous permet de préserver la confidentialité de manières non disponibles sur la blockchain elle-même. Avec les preuves à divulgation nulle de connaissance, nous pouvons garantir l'intégrité et éventuellement rendre économiquement avantageux pour quiconque exécute le composant centralisé de maintenir la disponibilité. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). + +## Remerciements {#acknowledgements} + +- Josh Crites a lu une ébauche de cet article et m'a aidé avec un problème épineux de Noir. + +Toute erreur restante est de ma responsabilité. diff --git a/public/content/translations/fr/developers/tutorials/calling-a-smart-contract-from-javascript/index.md b/public/content/translations/fr/developers/tutorials/calling-a-smart-contract-from-javascript/index.md index 95005ce720f..dd997c04818 100644 --- a/public/content/translations/fr/developers/tutorials/calling-a-smart-contract-from-javascript/index.md +++ b/public/content/translations/fr/developers/tutorials/calling-a-smart-contract-from-javascript/index.md @@ -1,12 +1,8 @@ --- title: Appeler un contrat intelligent depuis JavaScript -description: Comment appeler une fonction de contrat intelligent à partir de JavaScript en utilisant un exemple de jeton Dai +description: "Comment appeler une fonction de contrat intelligent à partir de JavaScript en utilisant un exemple de jeton Dai" author: jdourlens -tags: - - "transactions" - - "frontend" - - "JavaScript" - - "web3.js" +tags: [ "transactions", "frontend", "JavaScript", "web3.js" ] skill: beginner lang: fr published: 2020-04-19 @@ -15,15 +11,15 @@ sourceUrl: https://ethereumdev.io/calling-a-smart-contract-from-javascript/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -Dans ce tutoriel, nous allons voir comment appeler une fonction de [contrat intelligent](/developers/docs/smart-contracts/) à partir de JavaScript. La première consiste à lire l'état d'un contrat intelligent (par exemple, le solde d'un détenteur d'ERC20), puis nous modifierons l'état de la blockchain en effectuant un transfert de jeton. Vous devriez déjà être familiarisé avec [la configuration d'un environnement JS pour interagir avec la blockchain](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/). +Dans ce tutoriel, nous verrons comment appeler une fonction de [contrat intelligent](/developers/docs/smart-contracts/) à partir de JavaScript. La première étape consiste à lire l'état d'un contrat intelligent (par exemple, le solde d'un détenteur d'ERC20), puis nous modifierons l'état de la blockchain en effectuant un transfert de jeton. Vous devriez déjà être familiarisé avec [la configuration d'un environnement JS pour interagir avec la blockchain](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/). -Pour cet exemple, nous allons jouer avec le jeton DAI, à des fins de test, nous allons créer une fourche de la blockchain en utilisant ganache-cli et débloquer une adresse qui a déjà beaucoup de DAI : +Pour cet exemple, nous allons jouer avec le jeton DAI. À des fins de test, nous allons créer une fourche de la blockchain en utilisant ganache-cli et déverrouiller une adresse qui possède déjà beaucoup de DAI : ```bash ganache-cli -f https://mainnet.infura.io/v3/[YOUR INFURA KEY] -d -i 66 1 --unlock 0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81 ``` -Pour interagir avec un contrat intelligent, nous avons besoin de son adresse et de son ABI : +Pour interagir avec un contrat intelligent, nous aurons besoin de son adresse et de son ABI : ```js const ERC20TransferABI = [ @@ -74,7 +70,7 @@ const ERC20TransferABI = [ const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f" ``` -Pour ce projet, nous avons dépouillé l'ABI ERC20 complet pour ne garder que les fonctions `balanceOf` et `transfer` mais vous pouvez trouver [l'ABI ERC20 complète ici](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). +Pour ce projet, nous avons réduit l'ABI ERC20 complet pour ne conserver que les fonctions `balanceOf` et `transfer`, mais vous pouvez trouver [l'ABI ERC20 complet ici](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). Nous devons ensuite instancier notre contrat intelligent : @@ -96,25 +92,25 @@ const receiverAddress = "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" Dans la partie suivante, nous allons appeler la fonction `balanceOf` pour récupérer la quantité actuelle de jetons que les deux adresses détiennent. -## Appel : Lire la valeur d'un contrat intelligent {#call-reading-value-from-a-smart-contract} +## Appel : lecture d'une valeur à partir d'un contrat intelligent {#call-reading-value-from-a-smart-contract} Le premier exemple appelle une méthode « constante » et exécute sa méthode de contrat intelligent dans l'EVM sans envoyer de transaction. Pour cela, nous allons lire le solde ERC20 d'une adresse. [Lisez notre article sur les jetons ERC20](/developers/tutorials/understand-the-erc-20-token-smart-contract/). -Vous pouvez accéder aux méthodes d'un contrat intelligent instancié pour lequel vous avez fourni l'ABI comme suit : `yourContract.methods.methodname`. En utilisant la fonction `call`, vous recevrez le résultat de l'exécution de la fonction. +Vous pouvez accéder aux méthodes d'un contrat intelligent instancié pour lesquelles vous avez fourni l'ABI comme suit : `yourContract.methods.methodname`. En utilisant la fonction `call`, vous recevrez le résultat de l'exécution de la fonction. ```js daiToken.methods.balanceOf(senderAddress).call(function (err, res) { if (err) { - console.log("An error occurred", err) + console.log("Une erreur s'est produite", err) return } - console.log("The balance is: ", res) + console.log("Le solde est : ", res) }) ``` -N'oubliez pas que le DAI ERC20 comporte 18 décimales, ce qui signifie que vous devez supprimer 18 zéros pour obtenir le montant correct. Les uint256 sont retournés sous forme de chaînes de caractères, car JavaScript ne gère pas les grandes valeurs numériques. Si vous ne savez pas [comment traiter les grands nombres dans JS, consultez notre tutoriel sur bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). +N'oubliez pas que le DAI ERC20 a 18 décimales, ce qui signifie que vous devez supprimer 18 zéros pour obtenir le montant correct. Les uint256 sont retournés sous forme de chaînes de caractères, car JavaScript ne gère pas les grandes valeurs numériques. Si vous n'êtes pas sûr de [comment gérer les grands nombres en JS, consultez notre tutoriel sur bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). -## Envoyer : Envoi d'une transaction à une fonction de contrat intelligent {#send-sending-a-transaction-to-a-smart-contract-function} +## Envoyer : envoi d'une transaction à une fonction de contrat intelligent {#send-sending-a-transaction-to-a-smart-contract-function} Pour le deuxième exemple, nous allons appeler la fonction de transfert du contrat intelligent DAI pour envoyer 10 DAI à notre deuxième adresse. La fonction de transfert accepte deux paramètres : l'adresse du destinataire et le montant du jeton à transférer. @@ -123,13 +119,13 @@ daiToken.methods .transfer(receiverAddress, "100000000000000000000") .send({ from: senderAddress }, function (err, res) { if (err) { - console.log("An error occurred", err) + console.log("Une erreur s'est produite", err) return } - console.log("Hash of the transaction: " + res) + console.log("Hachage de la transaction : " + res) }) ``` -La fonction d'appel renvoie le hachage de la transaction qui sera miné dans la blockchain. Sur Ethereum, les hachages de transaction sont prévisibles - c'est ainsi que nous pouvons obtenir le hachage de la transaction avant qu'elle ne soit exécutée ([apprenez comment les hachages sont calculés ici](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). +La fonction d'appel renvoie le hachage de la transaction qui sera minée dans la blockchain. Sur Ethereum, les hachages de transaction sont prévisibles ; c'est ainsi que nous pouvons obtenir le hachage de la transaction avant son exécution ([apprenez ici comment les hachages sont calculés](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). -Comme la fonction ne fait que soumettre la transaction à la blockchain, nous ne pouvons pas voir le résultat avant de savoir quand elle est minée et incluse dans la blockchain. Dans le prochain tutoriel, nous apprendrons[comment attendre pour qu'une transaction soit exécutée sur la blockchain en connaissant son hachage](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). +Comme la fonction ne fait que soumettre la transaction à la blockchain, nous ne pouvons pas voir le résultat tant que nous ne savons pas quand elle est minée et incluse dans la blockchain. Dans le prochain tutoriel, nous apprendrons [comment attendre qu'une transaction soit exécutée sur la blockchain en connaissant son hachage](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). diff --git a/public/content/translations/fr/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md b/public/content/translations/fr/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md new file mode 100644 index 00000000000..bb342444ec4 --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md @@ -0,0 +1,585 @@ +--- +title: "Construire une interface utilisateur pour votre contrat" +description: "En utilisant des composants modernes tels que TypeScript, React, Vite et Wagmi, nous allons passer en revue une interface utilisateur moderne, mais minimale, et apprendre à connecter un portefeuille à l'interface utilisateur, à appeler un contrat intelligent pour lire des informations, à envoyer une transaction à un contrat intelligent et à surveiller les événements d'un contrat intelligent pour identifier les changements." +author: Ori Pomerantz +tags: [ "typescript", "react", "vite", "wagmi", "frontend" ] +skill: beginner +published: 2023-11-01 +lang: fr +sidebarDepth: 3 +--- + +Vous avez trouvé une fonctionnalité dont nous avons besoin dans l'écosystème Ethereum. Vous avez écrit les contrats intelligents pour la mettre en œuvre, et peut-être même du code connexe qui s'exécute hors chaîne. C'est génial ! Malheureusement, sans interface utilisateur, vous n'aurez aucun utilisateur, et la dernière fois que vous avez écrit un site web, les gens utilisaient des modems commutés et JavaScript était nouveau. + +Cet article est pour vous. Je suppose que vous connaissez la programmation, et peut-être un peu de JavaScript et de HTML, mais que vos compétences en matière d'interface utilisateur sont rouillées et obsolètes. Ensemble, nous allons passer en revue une application moderne simple pour que vous puissiez voir comment on fait de nos jours. + +## Pourquoi est-ce important {#why-important} + +En théorie, vous pourriez simplement demander aux gens d'utiliser [Etherscan](https://holesky.etherscan.io/address/0x432d810484add7454ddb3b5311f0ac2e95cecea8#writeContract) ou [Blockscout](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=write_contract) pour interagir avec vos contrats. Ce sera formidable pour les Éthériens expérimentés. Mais nous essayons de servir [un autre milliard de personnes](https://blog.ethereum.org/2021/05/07/ethereum-for-the-next-billion). Cela ne se produira pas sans une excellente expérience utilisateur, et une interface utilisateur conviviale en est une grande partie. + +## Application Greeter {#greeter-app} + +Il y a beaucoup de théorie derrière le fonctionnement d'une interface utilisateur moderne, et [beaucoup de bons sites](https://react.dev/learn/thinking-in-react) [qui l'expliquent](https://wagmi.sh/core/getting-started). Au lieu de répéter l'excellent travail effectué par ces sites, je vais supposer que vous préférez apprendre par la pratique et commencer par une application avec laquelle vous pouvez jouer. Vous avez encore besoin de la théorie pour faire avancer les choses, et nous y viendrons - nous allons simplement parcourir les fichiers sources un par un, et discuter des choses au fur et à mesure que nous les abordons. + +### Installation {#installation} + +1. Si nécessaire, ajoutez [la blockchain Holesky](https://chainlist.org/?search=holesky&testnets=true) à votre portefeuille et [obtenez des ETH de test](https://www.holeskyfaucet.io/). + +2. Clonez le dépôt GitHub. + + ```sh + git clone https://github.com/qbzzt/20230801-modern-ui.git + ``` + +3. Installez les paquets nécessaires. + + ```sh + cd 20230801-modern-ui + pnpm install + ``` + +4. Démarrez l'application. + + ```sh + pnpm dev + ``` + +5. Naviguez vers l'URL affichée par l'application. Dans la plupart des cas, c'est [http://localhost:5173/](http://localhost:5173/). + +6. Vous pouvez voir le code source du contrat, une version légèrement modifiée du Greeter de Hardhat, [sur un explorateur de blockchain](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contract). + +### Présentation des fichiers {#file-walk-through} + +#### `index.html` {#index-html} + +Ce fichier est un modèle HTML standard, à l'exception de cette ligne, qui importe le fichier de script. + +```html + +``` + +#### `src/main.tsx` {#main-tsx} + +L'extension du fichier nous indique que ce fichier est un [composant React](https://www.w3schools.com/react/react_components.asp) écrit en [TypeScript](https://www.typescriptlang.org/), une extension de JavaScript qui prend en charge la [vérification de type](https://en.wikipedia.org/wiki/Type_system#Type_checking). TypeScript est compilé en JavaScript, nous pouvons donc l'utiliser pour l'exécution côté client. + +```tsx +import '@rainbow-me/rainbowkit/styles.css' +import { RainbowKitProvider } from '@rainbow-me/rainbowkit' +import * as React from 'react' +import * as ReactDOM from 'react-dom/client' +import { WagmiConfig } from 'wagmi' +import { chains, config } from './wagmi' +``` + +Importez le code de la bibliothèque dont nous avons besoin. + +```tsx +import { App } from './App' +``` + +Importez le composant React qui met en œuvre l'application (voir ci-dessous). + +```tsx +ReactDOM.createRoot(document.getElementById('root')!).render( +``` + +Créez le composant React racine. Le paramètre de `render` est [JSX](https://www.w3schools.com/react/react_jsx.asp), un langage d'extension qui utilise à la fois HTML et JavaScript/TypeScript. Le point d'exclamation ici indique au composant TypeScript : « vous ne savez pas que `document.getElementById('root')` sera un paramètre valide pour `ReactDOM.createRoot`, mais ne vous inquiétez pas - je suis le développeur et je vous dis qu'il y en aura un ». + +```tsx + +``` + +L'application se trouve à l'intérieur d'un [composant `React.StrictMode`](https://react.dev/reference/react/StrictMode). Ce composant indique à la bibliothèque React d'insérer des vérifications de débogage supplémentaires, ce qui est utile pendant le développement. + +```tsx + +``` + +L'application se trouve également à l'intérieur d'un [composant `WagmiConfig`](https://wagmi.sh/react/api/WagmiProvider). [La bibliothèque wagmi (we are going to make it)](https://wagmi.sh/) connecte les définitions de l'interface utilisateur React avec [la bibliothèque viem](https://viem.sh/) pour l'écriture d'une application décentralisée Ethereum. + +```tsx + +``` + +Et enfin, [un composant `RainbowKitProvider`](https://www.rainbowkit.com/). Ce composant gère la connexion et la communication entre le portefeuille et l'application. + +```tsx + +``` + +Nous pouvons maintenant avoir le composant pour l'application, qui met réellement en œuvre l'interface utilisateur. Le `/>` à la fin du composant indique à React que ce composant n'a pas de définitions à l'intérieur, conformément à la norme XML. + +```tsx + + + , +) +``` + +Bien sûr, nous devons fermer les autres composants. + +#### `src/App.tsx` {#app-tsx} + +```tsx +import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useAccount } from 'wagmi' +import { Greeter } from './components/Greeter' + +export function App() { +``` + +C'est la manière standard de créer un composant React - définir une fonction qui est appelée chaque fois qu'elle doit être rendue. Cette fonction a généralement du code TypeScript ou JavaScript en haut, suivi d'une instruction `return` qui renvoie le code JSX. + +```tsx + const { isConnected } = useAccount() +``` + +Ici, nous utilisons [`useAccount`](https://wagmi.sh/react/api/hooks/useAccount) pour vérifier si nous sommes connectés à une blockchain via un portefeuille ou non. + +Par convention, dans React, les fonctions appelées `use...` sont des [hooks](https://www.w3schools.com/react/react_hooks.asp) qui renvoient un certain type de données. Lorsque vous utilisez de tels hooks, non seulement votre composant obtient les données, mais lorsque ces données changent, le composant est re-rendu avec les informations mises à jour. + +```tsx + return ( + <> +``` + +Le JSX d'un composant React _doit_ renvoyer un seul composant. Lorsque nous avons plusieurs composants et que nous n'avons rien qui les englobe « naturellement », nous utilisons un composant vide (`<> ...` `) pour en faire un seul composant. + +```tsx +

Greeter

+ +``` + +Nous obtenons [le composant `ConnectButton`](https://www.rainbowkit.com/docs/connect-button) de RainbowKit. Lorsque nous ne sommes pas connectés, il nous donne un bouton `Connect Wallet` qui ouvre une fenêtre modale qui explique les portefeuilles et vous permet de choisir celui que vous utilisez. Lorsque nous sommes connectés, il affiche la blockchain que nous utilisons, l'adresse de notre compte et notre solde d'ETH. Nous pouvons utiliser ces affichages pour changer de réseau ou pour nous déconnecter. + +```tsx + {isConnected && ( +``` + +Lorsque nous devons insérer du JavaScript réel (ou du TypeScript qui sera compilé en JavaScript) dans un JSX, nous utilisons des accolades (`{}`). + +La syntaxe `a && b` est un raccourci pour [`a ?` b : a`](https://www.w3schools.com/react/react_es6_ternary.asp). C'est-à-dire que si `a`est vrai, il est évalué à`b`et sinon, il est évalué à`a`(qui peut être`false`, `0`, etc.). C'est un moyen facile d'indiquer à React qu'un composant ne doit être affiché que si une certaine condition est remplie. + +Dans ce cas, nous ne voulons montrer à l'utilisateur `Greeter` que si l'utilisateur est connecté à une blockchain. + +```tsx + + )} + + ) +} +``` + +#### `src/components/Greeter.tsx` {#greeter-tsx} + +Ce fichier contient la plupart des fonctionnalités de l'interface utilisateur. Il inclut des définitions qui seraient normalement dans plusieurs fichiers, mais comme il s'agit d'un tutoriel, le programme est optimisé pour être facile à comprendre la première fois, plutôt que pour la performance ou la facilité de maintenance. + +```tsx +import { useState, ChangeEventHandler } from 'react' +import { useNetwork, + useReadContract, + usePrepareContractWrite, + useContractWrite, + useContractEvent + } from 'wagmi' +``` + +Nous utilisons ces fonctions de bibliothèque. Encore une fois, elles sont expliquées ci-dessous là où elles sont utilisées. + +```tsx +import { AddressType } from 'abitype' +``` + +[La bibliothèque `abitype`](https://abitype.dev/) nous fournit des définitions TypeScript pour divers types de données Ethereum, tels que [`AddressType`](https://abitype.dev/config#addresstype). + +```tsx +let greeterABI = [ + . + . + . +] as const // greeterABI +``` + +L'ABI pour le contrat `Greeter`. +Si vous développez les contrats et l'interface utilisateur en même temps, vous les placez normalement dans le même dépôt et utilisez l'ABI généré par le compilateur Solidity comme fichier dans votre application. Cependant, ce n'est pas nécessaire ici car le contrat est déjà développé et ne va pas changer. + +```tsx +type AddressPerBlockchainType = { + [key: number]: AddressType +} +``` + +TypeScript est fortement typé. Nous utilisons cette définition pour spécifier l'adresse à laquelle le contrat `Greeter` est déployé sur différentes chaînes. La clé est un nombre (le chainId), et la valeur est un `AddressType` (une adresse). + +```tsx +const contractAddrs: AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' +} +``` + +L'adresse du contrat sur les deux réseaux pris en charge : [Holesky](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contact_code) et [Sepolia](https://eth-sepolia.blockscout.com/address/0x7143d5c190F048C8d19fe325b748b081903E3BF0?tab=contact_code). + +Note : Il y a en fait une troisième définition, pour Redstone Holesky, elle sera expliquée ci-dessous. + +```tsx +type ShowObjectAttrsType = { + name: string, + object: any +} +``` + +Ce type est utilisé comme paramètre pour le composant `ShowObject` (expliqué plus tard). Il inclut le nom de l'objet et sa valeur, qui sont affichés à des fins de débogage. + +```tsx +type ShowGreetingAttrsType = { + greeting: string | undefined +} +``` + +À tout moment, nous pouvons soit savoir quel est le message d'accueil (parce que nous l'avons lu sur la blockchain), soit ne pas le savoir (parce que nous ne l'avons pas encore reçu). Il est donc utile d'avoir un type qui peut être soit une chaîne de caractères, soit rien. + +##### Composant `Greeter` {#greeter-component} + +```tsx +const Greeter = () => { +``` + +Enfin, nous arrivons à la définition du composant. + +```tsx + const { chain } = useNetwork() +``` + +Informations sur la chaîne que nous utilisons, gracieuseté de [wagmi](https://wagmi.sh/react/hooks/useNetwork). +Comme il s'agit d'un hook (`use...`), chaque fois que cette information change, le composant est redessiné. + +```tsx + const greeterAddr = chain && contractAddrs[chain.id] +``` + +L'adresse du contrat Greeter, qui varie selon la chaîne (et qui est `undefined` si nous n'avons pas d'informations sur la chaîne ou si nous sommes sur une chaîne sans ce contrat). + +```tsx + const readResults = useReadContract({ + address: greeterAddr, + abi: greeterABI, + functionName: "greet" , // Pas d'arguments + watch: true + }) +``` + +[Le hook `useReadContract`](https://wagmi.sh/react/api/hooks/useReadContract) lit les informations d'un contrat. Vous pouvez voir exactement quelles informations il renvoie en développant `readResults` dans l'interface utilisateur. Dans ce cas, nous voulons qu'il continue à chercher afin d'être informés lorsque le message d'accueil change. + +**Note :** Nous pourrions écouter les [événements `setGreeting`](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=logs) pour savoir quand le message d'accueil change et le mettre à jour de cette manière. Cependant, bien que cela puisse être plus efficace, cela ne s'appliquera pas dans tous les cas. Lorsque l'utilisateur passe à une chaîne différente, le message d'accueil change également, mais ce changement n'est pas accompagné d'un événement. Nous pourrions avoir une partie du code qui écoute les événements et une autre qui identifie les changements de chaîne, mais ce serait plus compliqué que de simplement définir [le paramètre `watch`](https://wagmi.sh/react/api/hooks/useReadContract#watch-optional). + +```tsx + const [ newGreeting, setNewGreeting ] = useState("") +``` + +Le [`hook useState` de React](https://www.w3schools.com/react/react_usestate.asp) nous permet de spécifier une variable d'état, dont la valeur persiste d'un rendu du composant à un autre. La valeur initiale est le paramètre, dans ce cas la chaîne vide. + +Le hook `useState` renvoie une liste avec deux valeurs : + +1. La valeur actuelle de la variable d'état. +2. Une fonction pour modifier la variable d'état si nécessaire. Comme il s'agit d'un hook, chaque fois qu'il est appelé, le composant est à nouveau rendu. + +Dans ce cas, nous utilisons une variable d'état pour le nouveau message d'accueil que l'utilisateur veut définir. + +```tsx + const greetingChange : ChangeEventHandler = (evt) => + setNewGreeting(evt.target.value) +``` + +C'est le gestionnaire d'événements pour le changement du champ de saisie du nouveau message d'accueil. Le type, [`ChangeEventHandler`](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/), spécifie qu'il s'agit d'un gestionnaire pour un changement de valeur d'un élément d'entrée HTML. La partie `` est utilisée car il s'agit d'un [type générique](https://www.w3schools.com/typescript/typescript_basic_generics.php). + +```tsx + const preparedTx = usePrepareContractWrite({ + address: greeterAddr, + abi: greeterABI, + functionName: 'setGreeting', + args: [ newGreeting ] + }) + const workingTx = useContractWrite(preparedTx.config) +``` + +Voici le processus pour soumettre une transaction blockchain du point de vue du client : + +1. Envoyez la transaction à un nœud de la blockchain en utilisant [`eth_estimateGas`](https://docs.alchemy.com/reference/eth-estimategas). +2. Attendez une réponse du nœud. +3. Lorsque la réponse est reçue, demandez à l'utilisateur de signer la transaction via le portefeuille. Cette étape _doit_ avoir lieu après la réception de la réponse du nœud, car le coût en gaz de la transaction est montré à l'utilisateur avant qu'il ne la signe. +4. Attendez l'approbation de l'utilisateur. +5. Envoyez à nouveau la transaction, cette fois en utilisant [`eth_sendRawTransaction`](https://docs.alchemy.com/reference/eth-sendrawtransaction). + +L'étape 2 est susceptible de prendre un temps perceptible, pendant lequel les utilisateurs se demanderaient si leur commande a bien été reçue par l'interface utilisateur et pourquoi on ne leur demande pas déjà de signer la transaction. Cela crée une mauvaise expérience utilisateur (UX). + +La solution est d'utiliser des [hooks de préparation](https://wagmi.sh/react/prepare-hooks). Chaque fois qu'un paramètre change, envoyez immédiatement au nœud la requête `eth_estimateGas`. Ensuite, lorsque l'utilisateur veut réellement envoyer la transaction (dans ce cas en appuyant sur **Mettre à jour le message d'accueil**), le coût en gaz est connu et l'utilisateur peut voir la page du portefeuille immédiatement. + +```tsx + return ( +``` + +Nous pouvons maintenant enfin créer le HTML réel à renvoyer. + +```tsx + <> +

Greeter

+ { + !readResults.isError && !readResults.isLoading && + + } +
+``` + +Créez le composant `ShowGreeting` (expliqué ci-dessous), mais uniquement si le message d'accueil a été lu avec succès à partir de la blockchain. + +```tsx + +``` + +C'est le champ de saisie de texte où l'utilisateur peut définir un nouveau message d'accueil. Chaque fois que l'utilisateur appuie sur une touche, nous appelons `greetingChange` qui appelle `setNewGreeting`. Comme `setNewGreeting` provient du hook `useState`, il provoque un nouveau rendu du composant `Greeter`. Cela signifie que : + +- Nous devons spécifier `value` pour conserver la valeur du nouveau message d'accueil, sinon il reviendrait à la valeur par défaut, la chaîne vide. +- `usePrepareContractWrite` est appelé chaque fois que `newGreeting` change, ce qui signifie qu'il aura toujours le dernier `newGreeting` dans la transaction préparée. + +```tsx + +``` + +S'il n'y a pas de `workingTx.write`, nous attendons toujours les informations nécessaires pour envoyer la mise à jour du message d'accueil, donc le bouton est désactivé. S'il y a une valeur `workingTx.write`, alors c'est la fonction à appeler pour envoyer la transaction. + +```tsx +
+ + + + + ) +} +``` + +Enfin, pour vous aider à voir ce que nous faisons, montrez les trois objets que nous utilisons : + +- `readResults` +- `preparedTx` +- `workingTx` + +##### Composant `ShowGreeting` {#showgreeting-component} + +Ce composant montre + +```tsx +const ShowGreeting = (attrs : ShowGreetingAttrsType) => { +``` + +Une fonction de composant reçoit un paramètre avec tous les attributs du composant. + +```tsx + return {attrs.greeting} +} +``` + +##### Composant `ShowObject` {#showobject-component} + +À titre d'information, nous utilisons le composant `ShowObject` pour montrer les objets importants (`readResults` pour la lecture du message d'accueil et `preparedTx` et `workingTx` pour les transactions que nous créons). + +```tsx +const ShowObject = (attrs: ShowObjectAttrsType ) => { + const keys = Object.keys(attrs.object) + const funs = keys.filter(k => typeof attrs.object[k] == "function") + return <> +
+``` + +Nous ne voulons pas encombrer l'interface utilisateur avec toutes les informations, donc pour permettre de les voir ou de les fermer, nous utilisons une balise [`details`](https://www.w3schools.com/tags/tag_details.asp). + +```tsx + {attrs.name} +
+        {JSON.stringify(attrs.object, null, 2)}
+```
+
+La plupart des champs sont affichés en utilisant [`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp).
+
+```tsx
+      
+ { funs.length > 0 && + <> + Fonctions : +
    +``` + +L'exception concerne les fonctions, qui ne font pas partie de [la norme JSON](https://www.json.org/json-en.html), elles doivent donc être affichées séparément. + +```tsx + {funs.map((f, i) => +``` + +Dans JSX, le code à l'intérieur des accolades `{` `}` est interprété comme du JavaScript. Ensuite, le code à l'intérieur des parenthèses `( )` est interprété à nouveau comme du JSX. + +```tsx + (
  • {f}
  • ) + )} +``` + +React exige que les balises dans [l'arbre DOM](https://www.w3schools.com/js/js_htmldom.asp) aient des identifiants distincts. Cela signifie que les enfants de la même balise (dans ce cas, [la liste non ordonnée](https://www.w3schools.com/tags/tag_ul.asp)), ont besoin d'attributs `key` différents. + +```tsx +
+ + } +
+ +} +``` + +Terminez les différentes balises HTML. + +##### L'exportation finale {#the-final-export} + +```tsx +export { Greeter } +``` + +Le composant `Greeter` est celui que nous devons exporter pour l'application. + +#### `src/wagmi.ts` {#wagmi-ts} + +Enfin, diverses définitions liées à WAGMI se trouvent dans `src/wagmi.ts`. Je ne vais pas tout expliquer ici, car la plupart est du code passe-partout que vous n'aurez probablement pas besoin de changer. + +Le code ici n'est pas exactement le même que [sur GitHub](https://github.com/qbzzt/20230801-modern-ui/blob/main/src/wagmi.ts) car plus tard dans l'article, nous ajoutons une autre chaîne ([Redstone Holesky](https://redstone.xyz/docs/network-info)). + +```ts +import { getDefaultWallets } from '@rainbow-me/rainbowkit' +import { configureChains, createConfig } from 'wagmi' +import { holesky, sepolia } from 'wagmi/chains' +``` + +Importez les blockchains que l'application prend en charge. Vous pouvez voir la liste des chaînes prises en charge [dans le GitHub de viem](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions). + +```ts +import { publicProvider } from 'wagmi/providers/public' + +const walletConnectProjectId = 'c96e690bb92b6311e8e9b2a6a22df575' +``` + +Pour pouvoir utiliser [WalletConnect](https://walletconnect.com/), vous avez besoin d'un ID de projet pour votre application. Vous pouvez l'obtenir sur [cloud.walletconnect.com](https://cloud.walletconnect.com/sign-in). + +```ts +const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia ], + [ + publicProvider(), + ], +) + +const { connectors } = getDefaultWallets({ + appName: 'My wagmi + RainbowKit App', + chains, + projectId: walletConnectProjectId, +}) + +export const config = createConfig({ + autoConnect: true, + connectors, + publicClient, + webSocketPublicClient, +}) + +export { chains } +``` + +### Ajouter une autre blockchain {#add-blockchain} + +De nos jours, il existe de nombreuses [solutions de mise à l'échelle de couche 2](/layer-2/), et vous pourriez vouloir en prendre en charge certaines que viem ne prend pas encore en charge. Pour ce faire, vous devez modifier `src/wagmi.ts`. Ces instructions expliquent comment ajouter [Redstone Holesky](https://redstone.xyz/docs/network-info). + +1. Importez le type `defineChain` depuis viem. + + ```ts + import { defineChain } from 'viem' + ``` + +2. Ajoutez la définition du réseau. + + ```ts + const redstoneHolesky = defineChain({ + id: 17_001, + name: 'Redstone Holesky', + network: 'redstone-holesky', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + public: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + }, + blockExplorers: { + default: { name: 'Explorer', url: 'https://explorer.holesky.redstone.xyz' }, + }, + }) + ``` + +3. Ajoutez la nouvelle chaîne à l'appel `configureChains`. + + ```ts + const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia, redstoneHolesky ], + [ publicProvider(), ], + ) + ``` + +4. Assurez-vous que l'application connaît l'adresse de vos contrats sur le nouveau réseau. Dans ce cas, nous modifions `src/components/Greeter.tsx` : + + ```ts + const contractAddrs : AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Redstone Holesky + 17001: '0x4919517f82a1B89a32392E1BF72ec827ba9986D3', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' + } + ``` + +## Conclusion {#conclusion} + +Bien sûr, vous ne vous souciez pas vraiment de fournir une interface utilisateur pour `Greeter`. Vous voulez créer une interface utilisateur pour vos propres contrats. Pour créer votre propre application, suivez ces étapes : + +1. Spécifiez la création d'une application wagmi. + + ```sh copy + pnpm create wagmi + ``` + +2. Nommez l'application. + +3. Sélectionnez le framework **React**. + +4. Sélectionnez la variante **Vite**. + +5. Vous pouvez [ajouter le kit Rainbow](https://www.rainbowkit.com/docs/installation#manual-setup). + +Maintenant, allez rendre vos contrats utilisables pour le monde entier. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). + diff --git a/public/content/translations/fr/developers/tutorials/deploying-your-first-smart-contract/index.md b/public/content/translations/fr/developers/tutorials/deploying-your-first-smart-contract/index.md index 5944d2fa576..1b3754c8925 100644 --- a/public/content/translations/fr/developers/tutorials/deploying-your-first-smart-contract/index.md +++ b/public/content/translations/fr/developers/tutorials/deploying-your-first-smart-contract/index.md @@ -1,12 +1,14 @@ --- -title: Déployer votre premier contrat intelligent -description: Introduction au déploiement de votre premier contrat intelligent sur le réseau de test Ethereum +title: "Déployer votre premier contrat intelligent" +description: "Introduction au déploiement de votre premier contrat intelligent sur un réseau de test Ethereum" author: "jdourlens" tags: - - "contrats intelligents" - - "remix" - - "solidity" - - "déploiement" + [ + "contrats intelligents", + "remix", + "solidité", + "déploiement" + ] skill: beginner lang: fr published: 2020-04-03 @@ -17,15 +19,15 @@ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" Je suppose que vous êtes aussi enthousiaste que nous à l'idée de [déployer](/developers/docs/smart-contracts/deploying/) et d'interagir avec votre premier [contrat intelligent](/developers/docs/smart-contracts/) sur la blockchain Ethereum. -Pas d'inquiétude, comme il s'agit de notre premier contrat, nous le déploierons sur un [réseau de test local](/developers/docs/networks/) afin qu'il ne vous coûte rien de le déployer et de vous amuser autant que vous le souhaitez avec. +Ne vous inquiétez pas, comme il s'agit de notre premier contrat intelligent, nous le déploierons sur un [réseau de test local](/developers/docs/networks/) afin que son déploiement et son utilisation ne vous coûtent rien et que vous puissiez l'utiliser autant que vous le souhaitez. ## Rédiger notre contrat {#writing-our-contract} -La première étape est de [visiter Remix](https://remix.ethereum.org/) et de créer un nouveau fichier. Dans la partie supérieure gauche de l'interface Remix ajoutez un nouveau fichier et entrez le nom de fichier que vous voulez. +La première étape consiste à [visiter Remix](https://remix.ethereum.org/) et à créer un nouveau fichier. Dans la partie supérieure gauche de l'interface Remix, ajoutez un nouveau fichier et saisissez le nom de fichier que vous souhaitez. ![Ajout d'un nouveau fichier dans l'interface Remix](./remix.png) -Dans ce nouveau fichier, nous collerons le code suivant. +Dans le nouveau fichier, nous allons coller le code suivant. ```solidity // SPDX-License-Identifier: MIT @@ -33,15 +35,15 @@ pragma solidity >=0.5.17; contract Counter { - // Public variable of type unsigned int to keep the number of counts + // Variable publique de type entier non signé pour conserver le nombre de comptages uint256 public count = 0; - // Function that increments our counter + // Fonction qui incrémente notre compteur function increment() public { count += 1; } - // Not necessary getter to get the count value + // Accesseur non nécessaire pour obtenir la valeur du compteur function getCount() public view returns (uint256) { return count; } @@ -49,51 +51,51 @@ contract Counter { } ``` -Si vous êtes un habitué de la programmation, vous pouvez facilement deviner ce que fait ce programme. Voici une explication ligne par ligne : +Si vous êtes habitué à la programmation, vous pouvez facilement deviner ce que fait ce programme. Voici une explication ligne par ligne : -- Ligne 4 : Nous définissons un contrat portant le nom `Counter`. -- Ligne 7 : Notre contrat stocke un entier non signé nommé `count` commençant à 0. -- Ligne 10 : La première fonction va modifier l'état du contrat et `incrémenter()` notre variable `count`. -- Ligne 15 : La seconde fonction permet de lire la valeur de la variable `count` en dehors du contrat intelligent. Notez que, comme nous avons défini notre variable `count` comme étant publique, ce n'est pas nécessaire mais est montré comme exemple. +- Ligne 4 : nous définissons un contrat avec le nom `Counter`. +- Ligne 7 : notre contrat stocke un entier non signé nommé `count` commençant à 0. +- Ligne 10 : la première fonction modifiera l'état du contrat et `increment()` notre variable `count`. +- Ligne 15 : la seconde fonction est juste un accesseur (« getter ») pour pouvoir lire la valeur de la variable `count` en dehors du contrat intelligent. Notez que, comme nous avons défini notre variable `count` comme publique, cela n'est pas nécessaire mais est montré à titre d'exemple. -Tout cela pour notre premier contrat intelligent simple. Comme vous le savez peut-être, il ressemble à une classe des langages OOP (Object-Oriented Programming) comme Java ou C++. Il est maintenant temps de jouer avec notre contrat. +C'est tout pour notre premier contrat intelligent simple. Comme vous le savez peut-être, cela ressemble à une classe de langages POO (programmation orientée objet) comme Java ou C++. Il est maintenant temps d'interagir avec notre contrat. -## Déployer notre contract {#deploying-our-contract} +## Déployer notre contrat {#deploying-our-contract} -Notre tout premier contrat intelligent ayant été rédigé, nous allons maintenant le déployer sur la blockchain pour pouvoir jouer avec lui. +Maintenant que nous avons écrit notre tout premier contrat intelligent, nous allons le déployer sur la blockchain pour pouvoir interagir avec. -[Déployer le contrat intelligent sur la blockchain](/developers/docs/smart-contracts/deploying/) consiste en fait à envoyer une transaction contenant le code du contrat intelligent compilé sans spécifier de destinataires. +[Déployer le contrat intelligent sur la blockchain](/developers/docs/smart-contracts/deploying/) consiste en fait simplement à envoyer une transaction contenant le code du contrat intelligent compilé sans spécifier de destinataires. -Nous allons d'abord [compiler le contrat](/developers/docs/smart-contracts/compiling/) en cliquant sur l'icône de compilation sur le côté gauche : +Nous allons d'abord [compiler le contrat](/developers/docs/smart-contracts/compiling/) en cliquant sur l'icône de compilation sur le côté gauche : -![L'icône de compilation dans la barre d'outils Remix](./remix-compile-button.png) +![L'icône de compilation dans la barre d'outils de Remix](./remix-compile-button.png) -Cliquez ensuite sur le bouton compiler : +Cliquez ensuite sur le bouton de compilation : -![Le bouton de compilation dans le compilateur solidity de Remix](./remix-compile.png) +![Le bouton de compilation dans le compilateur Solidity de Remix](./remix-compile.png) -Vous pouvez choisir de sélectionner l’option "Compilation automatique" pour que le contrat soit toujours compilé lorsque vous enregistrez le contenu dans l’éditeur de texte. +Vous pouvez choisir de sélectionner l'option « Auto compile » afin que le contrat soit toujours compilé lorsque vous sauvegardez le contenu dans l'éditeur de texte. -Ensuite, accédez à l'écran "déployer et executer" des transactions : +Ensuite, naviguez vers l'écran « déployer et exécuter les transactions » : -![L'icône deployer dans la barre d'outils Remix](./remix-deploy.png) +![L'icône de déploiement dans la barre d'outils Remix](./remix-deploy.png) -Une fois que vous êtes sur l’écran "déployer et exécuter", vérifiez que le nom de votre contrat apparaît et cliquez sur déployer. Comme vous pouvez le voir en haut de la page, l'environnement actuel est "JavaScript VM", ce qui signifie que nous allons déployer et interagir avec notre contrat intelligent sur une blockchain de test locale pour être en mesure de tester plus rapidement et sans frais. +Une fois que vous êtes sur l'écran « déployer et exécuter les transactions », vérifiez bien que le nom de votre contrat apparaisse et cliquez sur Déployer. Comme vous pouvez le voir en haut de la page, l'environnement actuel est « JavaScript VM », ce qui signifie que nous allons déployer et interagir avec notre contrat intelligent sur une blockchain de test locale pour pouvoir tester plus rapidement et sans frais. -![Le bouton deployer dans le compilateur solidity de Remix](./remix-deploy-button.png) +![Le bouton de déploiement dans le compilateur Solidity de Remix](./remix-deploy-button.png) -Une fois que vous avez cliqué sur le bouton « Déployer », vous verrez votre contrat apparaître en bas de page. Cliquez sur la flèche à gauche pour la développer pour voir le contenu de notre contrat. Ceci est notre variable `counter`, notre fonction `increment()` et l'accesseur `getCounter()`. +Une fois que vous avez cliqué sur le bouton « Déployer », vous verrez votre contrat apparaître en bas. Cliquez sur la flèche à gauche pour le développer afin de voir le contenu de notre contrat. Il s'agit de notre variable `counter`, de notre fonction `increment()` et de l'accesseur `getCounter()`. -Si vous cliquez sur le bouton `count` ou `getCount` , il récupérera le contenu de la variable `count` du contrat et l'affichera. Comme nous n'avons pas encore appelé la fonction `incrément` , elle devrait afficher 0. +Si vous cliquez sur le bouton `count` ou `getCount`, le contenu de la variable `count` du contrat sera récupéré et affiché. Comme nous n'avons pas encore appelé la fonction `increment`, la valeur 0 devrait s'afficher. -![Le bouton de fonction dans le compilateur solidity de Remix](./remix-function-button.png) +![Le bouton de fonction dans le compilateur Solidity de Remix](./remix-function-button.png) -Appelons maintenant la fonction `increment` en cliquant sur le bouton. Vous verrez les journaux des transactions terminées apparaitre en bas de la fenêtre. Vous verrez que les journaux sont différents si vous appuyez sur le bouton de récupération des données plutôt que sur le bouton `increment`. C’est parce que la lecture des données sur la blockchain ne requiert ni transactions (écriture) ni frais. En effet, seule la modification de l'état de la blockchain nécessite de faire une transaction : +Appelons maintenant la fonction `increment` en cliquant sur le bouton. Vous verrez les journaux des transactions effectuées apparaître en bas de la fenêtre. Vous verrez que les journaux sont différents lorsque vous appuyez sur le bouton pour récupérer les données, par opposition au bouton `increment`. C'est parce que la lecture des données sur la blockchain ne nécessite aucune transaction (écriture) ni aucun frais. En effet, seule la modification de l'état de la blockchain nécessite d'effectuer une transaction : -![Un journal des transactions](./transaction-log.png) +![Un journal de transactions](./transaction-log.png) -Après avoir appuyé sur le bouton increment qui va générer une transaction pour appeler notre fonction `increment()` si nous cliquons de nouveau sur le boutons count ou getCount, nous allons lire l'état récemment mis à jour de notre contrat intelligent avec une variable count supérieure à 0. +Après avoir appuyé sur le bouton `increment` qui génère une transaction pour appeler notre fonction `increment()`, si nous cliquons à nouveau sur les boutons `count` ou `getCount`, nous lirons l'état nouvellement mis à jour de notre contrat intelligent, avec la variable `count` supérieure à 0. -![État du contrat intelligent récemment mis à jour](./updated-state.png) +![État nouvellement mis à jour du contrat intelligent](./updated-state.png) -Dans le prochain tutoriel, nous aborderons [comment vous pouvez ajouter des événements à vos contrats intelligents](/developers/tutorials/logging-events-smart-contracts/). La journalisation des événements est un moyen pratique de déboguer votre contrat intelligent et de comprendre ce qui se passe en appelant une fonction. +Dans le prochain tutoriel, nous verrons [comment vous pouvez ajouter des événements à vos contrats intelligents](/developers/tutorials/logging-events-smart-contracts/). La journalisation des événements est un moyen pratique de déboguer votre contrat intelligent et de comprendre ce qui se passe lors de l'appel d'une fonction. diff --git a/public/content/translations/fr/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md b/public/content/translations/fr/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md new file mode 100644 index 00000000000..2fb3d623ecc --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md @@ -0,0 +1,372 @@ +--- +title: "Comment développer et tester une dApp sur un réseau de test local multi-clients" +description: "Ce guide vous expliquera d'abord comment instancier et configurer un réseau de test Ethereum local multi-clients avant d'utiliser le réseau de test pour déployer et tester une dApp." +author: "Tedi Mitiku" +tags: + [ + "clients", + "nœuds", + "contrats intelligents", + "composabilité", + "couche de consensus", + "couche d'exécution", + "test" + ] +skill: intermediate +lang: fr +published: 2023-04-11 +--- + +## Introduction {#introduction} + +Ce guide vous explique comment instancier un réseau de test Ethereum local configurable, y déployer un contrat intelligent et utiliser ce réseau de test pour exécuter des tests sur votre dApp. Ce guide est conçu pour les développeurs de dApps qui souhaitent développer et tester leurs dApps localement avec différentes configurations de réseau avant de les déployer sur un réseau de test public ou sur le réseau principal. + +Dans ce guide, vous allez : + +- Instancier un réseau de test Ethereum local avec le [`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package) en utilisant [Kurtosis](https://www.kurtosis.com/), +- Connecter votre environnement de développement de dApp Hardhat au réseau de test local pour compiler, déployer et tester une dApp, et +- Configurer le réseau de test local, y compris des paramètres comme le nombre de nœuds et les paires de clients EL/CL spécifiques, pour permettre des flux de travail de développement et de test sur diverses configurations de réseau. + +### Qu'est-ce que Kurtosis ? {#what-is-kurtosis} + +[Kurtosis](https://www.kurtosis.com/) est un système de construction composable conçu pour configurer des environnements de test multi-conteneurs. Il permet spécifiquement aux développeurs de créer des environnements reproductibles qui nécessitent une logique de configuration dynamique, comme les réseaux de test de blockchain. + +Dans ce guide, le paquet eth-network-package de Kurtosis lance un réseau de test Ethereum local avec prise en charge du client de couche d'exécution (EL) [`geth`](https://geth.ethereum.org/), ainsi que des clients de couche de consensus (CL) [`teku`](https://consensys.io/teku), [`lighthouse`](https://lighthouse.sigmaprime.io/) et [`lodestar`](https://lodestar.chainsafe.io/). Ce paquet sert d'alternative configurable et composable aux réseaux dans des cadres de développement comme Hardhat Network, Ganache et Anvil. Kurtosis offre aux développeurs un contrôle et une flexibilité accrus sur les réseaux de test qu'ils utilisent, ce qui est l'une des raisons principales pour lesquelles la [Fondation Ethereum a utilisé Kurtosis pour tester la Fusion](https://www.kurtosis.com/blog/testing-the-ethereum-merge) et continue de l'utiliser pour tester les mises à niveau du réseau. + +## Configuration de Kurtosis {#setting-up-kurtosis} + +Avant de continuer, assurez-vous d'avoir : + +- [Installé et démarré le moteur Docker](https://docs.kurtosis.com/install/#i-install--start-docker) sur votre machine locale +- [Installé la CLI Kurtosis](https://docs.kurtosis.com/install#ii-install-the-cli) (ou l'avoir mise à niveau vers la dernière version, si vous avez déjà installé la CLI) +- Installé [Node.js](https://nodejs.org/en), [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable), et [npx](https://www.npmjs.com/package/npx) (pour votre environnement de dApp) + +## Instanciation d'un réseau de test Ethereum local {#instantiate-testnet} + +Pour lancer un réseau de test Ethereum local, exécutez : + +```python +kurtosis --enclave local-eth-testnet run github.com/kurtosis-tech/eth-network-package +``` + +Remarque : cette commande nomme votre réseau « local-eth-testnet » à l'aide de l'indicateur `--enclave`. + +Kurtosis affichera les étapes qu'il exécute en arrière-plan pendant qu'il interprète, valide, puis exécute les instructions. À la fin, vous devriez voir une sortie qui ressemble à ce qui suit : + +```python +INFO[2023-04-04T18:09:44-04:00] ====================================================== +INFO[2023-04-04T18:09:44-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-04T18:09:44-04:00] ====================================================== +Name: local-eth-testnet +UUID: 39372d756ae8 +Status: RUNNING +Creation Time: Tue, 04 Apr 2023 18:09:03 EDT + +========================================= Files Artifacts ========================================= +UUID Name +d4085a064230 cl-genesis-data +1c62cb792e4c el-genesis-data +bd60489b73a7 genesis-generation-config-cl +b2e593fe5228 genesis-generation-config-el +d552a54acf78 geth-prefunded-keys +5f7e661eb838 prysm-password +054e7338bb59 validator-keystore-0 + +========================================== User Services ========================================== +UUID Name Ports Status +e20f129ee0c5 cl-client-0-beacon http: 4000/tcp -> RUNNING + metrics: 5054/tcp -> + tcp-discovery: 9000/tcp -> 127.0.0.1:54263 + udp-discovery: 9000/udp -> 127.0.0.1:60470 +a8b6c926cdb4 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:54267 RUNNING + metrics: 5064/tcp -> +d7b802f623e8 el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:54253 RUNNING + rpc: 8545/tcp -> 127.0.0.1:54251 + tcp-discovery: 30303/tcp -> 127.0.0.1:54254 + udp-discovery: 30303/udp -> 127.0.0.1:53834 + ws: 8546/tcp -> 127.0.0.1:54252 +514a829c0a84 prelaunch-data-generator-1680646157905431468 STOPPED +62bd62d0aa7a prelaunch-data-generator-1680646157915424301 STOPPED +05e9619e0e90 prelaunch-data-generator-1680646157922872635 STOPPED + +``` + +Félicitations ! Vous avez utilisé Kurtosis pour instancier un réseau de test Ethereum local, avec un client CL (`lighthouse`) et un client EL (`geth`), via Docker. + +### Résumé {#review-instantiate-testnet} + +Dans cette section, vous avez exécuté une commande qui a demandé à Kurtosis d'utiliser le [`eth-network-package` hébergé à distance sur GitHub](https://github.com/kurtosis-tech/eth-network-package) pour lancer un réseau de test Ethereum local dans une [Enclave](https://docs.kurtosis.com/advanced-concepts/enclaves/) Kurtosis. À l'intérieur de votre enclave, vous trouverez à la fois des « artefacts de fichiers » et des « services utilisateur ». + +Les [Artefacts de fichiers](https://docs.kurtosis.com/advanced-concepts/files-artifacts/) dans votre enclave incluent toutes les données générées et utilisées pour amorcer les clients EL et CL. Les données ont été créées à l'aide du service `prelaunch-data-generator` construit à partir de cette [image Docker](https://github.com/ethpandaops/ethereum-genesis-generator) + +Les services utilisateur affichent tous les services conteneurisés fonctionnant dans votre enclave. Vous remarquerez qu'un seul nœud, comprenant à la fois un client EL et un client CL, a été créé. + +## Connecter votre environnement de développement de dApp au réseau de test Ethereum local {#connect-your-dapp} + +### Configurer l'environnement de développement de la dApp {#set-up-dapp-env} + +Maintenant que vous disposez d'un réseau de test local en cours d'exécution, vous pouvez connecter votre environnement de développement de dApp pour utiliser votre réseau de test local. Le cadre de développement Hardhat sera utilisé dans ce guide pour déployer une dApp de blackjack sur votre réseau de test local. + +Pour configurer votre environnement de développement de dApp, clonez le dépôt qui contient notre exemple de dApp et installez ses dépendances, exécutez : + +```python +git clone https://github.com/kurtosis-tech/awesome-kurtosis.git && cd awesome-kurtosis/smart-contract-example && yarn +``` + +Le dossier [smart-contract-example](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example) utilisé ici contient la configuration typique pour un développeur de dApp utilisant le cadre de développement [Hardhat](https://hardhat.org/) : + +- [`contracts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/contracts) contient quelques contrats intelligents simples pour une dApp de Blackjack +- [`scripts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/scripts) contient un script pour déployer un contrat de jeton sur votre réseau Ethereum local +- [`test/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/test) contient un simple test .js pour votre contrat de jeton afin de confirmer que chaque joueur de notre dApp de Blackjack a reçu 1000 jetons frappés pour lui +- [`hardhat.config.ts`](https://github.com/kurtosis-tech/awesome-kurtosis/blob/main/smart-contract-example/hardhat.config.ts) configure votre installation Hardhat + +### Configurer Hardhat pour utiliser le réseau de test local {#configure-hardhat} + +Une fois votre environnement de développement de dApp configuré, vous allez maintenant connecter Hardhat pour utiliser le réseau de test Ethereum local généré à l'aide de Kurtosis. Pour ce faire, remplacez `<$YOUR_PORT>` dans la structure `localnet` de votre fichier de configuration `hardhat.config.ts` par le port de la sortie de l'URI rpc de n'importe quel service `el-client-`. Dans cet exemple, le port serait `64248`. Votre port sera différent. + +Exemple dans `hardhat.config.ts` : + +```js +localnet: { +url: 'http://127.0.0.1:<$YOUR_PORT>',// TODO : REMPLACEZ $YOUR_PORT PAR LE PORT D'UNE URI DE NŒUD PRODUITE PAR LE PAQUETAGE KURTOSIS DU RÉSEAU ETH + +// Il s'agit de clés privées associées à des comptes de test pré-financés créés par le paquetage eth-network-package +// +accounts: [ + "ef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2", + "48fcc39ae27a0e8bf0274021ae6ebd8fe4a0e12623d61464c498900b28feb567", + "7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31", + "b3c409b6b0b3aa5e65ab2dc1930534608239a478106acf6f3d9178e9f9b00b35", + "df9bb6de5d3dc59595bcaa676397d837ff49441d211878c024eabda2cd067c9f", + "7da08f856b5956d40a72968f93396f6acff17193f013e8053f6fbb6c08c194d6", + ], +}, +``` + +Une fois que vous avez enregistré votre fichier, votre environnement de développement de dApp Hardhat est maintenant connecté à votre réseau de test Ethereum local ! Vous pouvez vérifier que votre réseau de test fonctionne en exécutant : + +```python +npx hardhat balances --network localnet +``` + +La sortie devrait ressembler à ceci : + +```python +0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766 has balance 10000000000000000000000000 +0x4E9A3d9D1cd2A2b2371b8b3F489aE72259886f1A has balance 10000000000000000000000000 +0xdF8466f277964Bb7a0FFD819403302C34DCD530A has balance 10000000000000000000000000 +0x5c613e39Fc0Ad91AfDA24587e6f52192d75FBA50 has balance 10000000000000000000000000 +0x375ae6107f8cC4cF34842B71C6F746a362Ad8EAc has balance 10000000000000000000000000 +0x1F6298457C5d76270325B724Da5d1953923a6B88 has balance 10000000000000000000000000 +``` + +Cela confirme que Hardhat utilise votre réseau de test local et détecte les comptes pré-financés créés par le `eth-network-package`. + +### Déployer et tester votre dApp localement {#deploy-and-test-dapp} + +L'environnement de développement de la dApp étant entièrement connecté au réseau de test Ethereum local, vous pouvez maintenant exécuter des flux de travail de développement et de test sur votre dApp en utilisant le réseau de test local. + +Pour compiler et déployer le contrat intelligent `ChipToken.sol` pour le prototypage et le développement local, exécutez : + +```python +npx hardhat compile +npx hardhat run scripts/deploy.ts --network localnet +``` + +La sortie devrait ressembler à : + +```python +ChipToken déployé à : 0xAb2A01BC351770D09611Ac80f1DE076D56E0487d +``` + +Essayez maintenant d'exécuter le test `simple.js` sur votre dApp locale pour confirmer que chaque joueur de notre dApp de Blackjack a reçu 1000 jetons frappés pour lui : + +La sortie devrait ressembler à ceci : + +```python +npx hardhat test --network localnet +``` + +La sortie devrait ressembler à ceci : + +```python +ChipToken + mint + ✔ devrait frapper 1000 jetons pour PLAYER ONE + + 1 réussi (654ms) +``` + +### Résumé {#review-dapp-workflows} + +À ce stade, vous avez configuré un environnement de développement de dApp, l'avez connecté à un réseau Ethereum local créé par Kurtosis, et avez compilé, déployé et exécuté un test simple sur votre dApp. + +Explorons maintenant comment vous pouvez configurer le réseau sous-jacent pour tester nos dApps dans diverses configurations de réseau. + +## Configuration du réseau de test Ethereum local {#configure-testnet} + +### Modification des configurations du client et du nombre de nœuds {#configure-client-config-and-num-nodes} + +Votre réseau de test Ethereum local peut être configuré pour utiliser différentes paires de clients EL et CL, ainsi qu'un nombre variable de nœuds, en fonction du scénario et de la configuration réseau spécifique que vous souhaitez développer ou tester. Cela signifie qu'une fois configuré, vous pouvez lancer un réseau de test local personnalisé et l'utiliser pour exécuter les mêmes flux de travail (déploiement, tests, etc.) dans diverses configurations de réseau pour vous assurer que tout fonctionne comme prévu. Pour en savoir plus sur les autres paramètres que vous pouvez modifier, consultez ce lien. + +Essayez ! Vous pouvez transmettre diverses options de configuration au `eth-network-package` via un fichier JSON. Ce fichier JSON de paramètres réseau fournit les configurations spécifiques que Kurtosis utilisera pour configurer le réseau Ethereum local. + +Prenez le fichier de configuration par défaut et modifiez-le pour lancer trois nœuds avec différentes paires EL/CL : + +- Nœud 1 avec `geth`/`lighthouse` +- Nœud 2 avec `geth`/`lodestar` +- Nœud 3 avec `geth`/`teku` + +Cette configuration crée un réseau hétérogène d'implémentations de nœuds Ethereum pour tester votre dApp. Votre fichier de configuration devrait maintenant ressembler à : + +```yaml +{ + "participants": + [ + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lighthouse", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lodestar", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "teku", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + ], + "network_params": + { + "preregistered_validator_keys_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete", + "num_validator_keys_per_node": 64, + "network_id": "3151908", + "deposit_contract_address": "0x4242424242424242424242424242424242424242", + "seconds_per_slot": 12, + "genesis_delay": 120, + "capella_fork_epoch": 5, + }, +} +``` + +Chaque structure `participants` correspond à un nœud du réseau, donc 3 structures `participants` indiqueront à Kurtosis de lancer 3 nœuds dans votre réseau. Chaque structure `participants` vous permettra de spécifier la paire EL et CL utilisée pour ce nœud spécifique. + +La structure `network_params` configure les paramètres réseau qui sont utilisés pour créer les fichiers de genèse pour chaque nœud ainsi que d'autres paramètres comme les secondes par créneau du réseau. + +Enregistrez votre fichier de paramètres modifié dans le répertoire de votre choix (dans l'exemple ci-dessous, il est enregistré sur le bureau), puis utilisez-le pour exécuter votre paquetage Kurtosis en exécutant : + +```python +kurtosis clean -a && kurtosis run --enclave local-eth-testnet github.com/kurtosis-tech/eth-network-package "$(cat ~/eth-network-params.json)" +``` + +Remarque : la commande `kurtosis clean -a` est utilisée ici pour demander à Kurtosis de détruire l'ancien réseau de test et son contenu avant d'en démarrer un nouveau. + +Encore une fois, Kurtosis fonctionnera un instant et affichera les différentes étapes qui se déroulent. Finalement, la sortie devrait ressembler à : + +```python +Starlark code successfully run. No output was returned. +INFO[2023-04-07T11:43:16-04:00] ========================================================== +INFO[2023-04-07T11:43:16-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-07T11:43:16-04:00] ========================================================== +Name: local-eth-testnet +UUID: bef8c192008e +Status: RUNNING +Creation Time: Fri, 07 Apr 2023 11:41:58 EDT + +========================================= Files Artifacts ========================================= +UUID Name +cc495a8e364a cl-genesis-data +7033fcdb5471 el-genesis-data +a3aef43fc738 genesis-generation-config-cl +8e968005fc9d genesis-generation-config-el +3182cca9d3cd geth-prefunded-keys +8421166e234f prysm-password +d9e6e8d44d99 validator-keystore-0 +23f5ba517394 validator-keystore-1 +4d28dea40b5c validator-keystore-2 + +========================================== User Services ========================================== +UUID Name Ports Status +485e6fde55ae cl-client-0-beacon http: 4000/tcp -> http://127.0.0.1:65010 RUNNING + metrics: 5054/tcp -> http://127.0.0.1:65011 + tcp-discovery: 9000/tcp -> 127.0.0.1:65012 + udp-discovery: 9000/udp -> 127.0.0.1:54455 +73739bd158b2 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:65016 RUNNING + metrics: 5064/tcp -> 127.0.0.1:65017 +1b0a233cd011 cl-client-1-beacon http: 4000/tcp -> 127.0.0.1:65021 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65023 + tcp-discovery: 9000/tcp -> 127.0.0.1:65024 + udp-discovery: 9000/udp -> 127.0.0.1:56031 + validator-metrics: 5064/tcp -> 127.0.0.1:65022 +949b8220cd53 cl-client-1-validator http: 4000/tcp -> 127.0.0.1:65028 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65030 + tcp-discovery: 9000/tcp -> 127.0.0.1:65031 + udp-discovery: 9000/udp -> 127.0.0.1:60784 + validator-metrics: 5064/tcp -> 127.0.0.1:65029 +c34417bea5fa cl-client-2 http: 4000/tcp -> 127.0.0.1:65037 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65035 + tcp-discovery: 9000/tcp -> 127.0.0.1:65036 + udp-discovery: 9000/udp -> 127.0.0.1:63581 +e19738e6329d el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:64986 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64988 + tcp-discovery: 30303/tcp -> 127.0.0.1:64987 + udp-discovery: 30303/udp -> 127.0.0.1:55706 + ws: 8546/tcp -> 127.0.0.1:64989 +e904687449d9 el-client-1 engine-rpc: 8551/tcp -> 127.0.0.1:64993 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64995 + tcp-discovery: 30303/tcp -> 127.0.0.1:64994 + udp-discovery: 30303/udp -> 127.0.0.1:58096 + ws: 8546/tcp -> 127.0.0.1:64996 +ad6f401126fa el-client-2 engine-rpc: 8551/tcp -> 127.0.0.1:65003 RUNNING + rpc: 8545/tcp -> 127.0.0.1:65001 + tcp-discovery: 30303/tcp -> 127.0.0.1:65000 + udp-discovery: 30303/udp -> 127.0.0.1:57269 + ws: 8546/tcp -> 127.0.0.1:65002 +12d04a9dbb69 prelaunch-data-generator-1680882122181135513 STOPPED +5b45f9c0504b prelaunch-data-generator-1680882122192182847 STOPPED +3d4aaa75e218 prelaunch-data-generator-1680882122201668972 STOPPED +``` + +Félicitations ! Vous avez configuré avec succès votre réseau de test local pour avoir 3 nœuds au lieu de 1. Pour exécuter les mêmes flux de travail que précédemment sur votre dApp (déploiement et test), effectuez les mêmes opérations que nous avons faites auparavant en remplaçant le `<$YOUR_PORT>` dans la structure `localnet` de votre fichier de configuration `hardhat.config.ts` par le port de la sortie de l'URI rpc de n'importe quel service `el-client-` dans votre nouveau réseau de test local à 3 nœuds. + +## Conclusion {#conclusion} + +Et c'est tout ! Pour résumer ce court guide, vous avez : + +- Créé un réseau de test Ethereum local sur Docker en utilisant Kurtosis +- Connecté votre environnement de développement de dApp local au réseau Ethereum local +- Déployé une dApp et exécuté un test simple sur celle-ci sur le réseau Ethereum local +- Configuré le réseau Ethereum sous-jacent pour avoir 3 nœuds + +Nous serions ravis d'avoir votre retour sur ce qui s'est bien passé pour vous, ce qui pourrait être amélioré, ou de répondre à vos questions. N'hésitez pas à nous contacter via [GitHub](https://github.com/kurtosis-tech/kurtosis/issues/new/choose) ou à [nous envoyer un e-mail](mailto:feedback@kurtosistech.com) ! + +### Autres exemples et guides {#other-examples-guides} + +Nous vous encourageons à consulter notre [guide de démarrage rapide](https://docs.kurtosis.com/quickstart) (où vous construirez une base de données Postgres et une API par-dessus) et nos autres exemples dans notre [dépôt awesome-kurtosis](https://github.com/kurtosis-tech/awesome-kurtosis) où vous trouverez d'excellents exemples, y compris des paquetages pour : + +- [Lancer le même réseau de test Ethereum local](https://github.com/kurtosis-tech/eth2-package), mais avec des services supplémentaires connectés tels qu'un spammeur de transactions (pour simuler des transactions), un moniteur de fourche, et une instance Grafana et Prometheus connectée +- Effectuer un [test de sous-réseautage](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/ethereum-network-partition-test) sur le même réseau Ethereum local diff --git a/public/content/translations/fr/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md b/public/content/translations/fr/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md index 715c6d08da5..522eca2cfe4 100644 --- a/public/content/translations/fr/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md +++ b/public/content/translations/fr/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md @@ -1,12 +1,9 @@ --- title: "Réduire la taille des contrats pour ne pas dépasser la limite" -description: Que pouvez-vous faire pour éviter que vos contrats intelligents ne deviennent trop volumineux ? +description: "Que pouvez-vous faire pour éviter que vos contrats intelligents ne deviennent trop volumineux ?" author: Markus Waas lang: fr -tags: - - "solidity" - - "contrats intelligents" - - "stockage" +tags: [ "solidité", "contrats intelligents", "stockage" ] skill: intermediate published: 2020-06-26 source: soliditydeveloper.com @@ -15,17 +12,17 @@ sourceUrl: https://soliditydeveloper.com/max-contract-size ## Pourquoi existe-t-il une limite ? {#why-is-there-a-limit} -Le [22 novembre 2016,](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/) la fourche Spurious Dragon a introduit [EIP-170](https://eips.ethereum.org/EIPS/eip-170), qui a ajouté une limite de taille des contrats intelligents de 24,576 kb. Pour vous, en tant que développeur Solidity, cela signifie que lorsque vous ajoutez de plus en plus de fonctionnalités à votre contrat, à un moment donné, vous atteindrez la limite et, lors du déploiement, vous rencontrerez cette erreur : +Le 22 novembre 2016, le [hard fork Spurious Dragon](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/) a introduit l'[EIP-170](https://eips.ethereum.org/EIPS/eip-170), qui a ajouté une limite de taille de contrat intelligent de 24,576 ko. Pour vous, en tant que développeur Solidity, cela signifie que lorsque vous ajoutez de plus en plus de fonctionnalités à votre contrat, à un moment donné, vous atteindrez la limite et, lors du déploiement, vous rencontrerez cette erreur : -`Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.` +`Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). Ce contrat pourrait ne pas être déployable sur le Réseau principal. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.` -Cette limite a été apportée pour empêcher les attaques par déni de service (DOS). Tout appel vers un contrat est relativement peu coûteux en gaz. Cependant, l'impact d'un appel de contrat sur les nœuds Ethereum augmente de manière exponentielle en fonction de la taille du code du contrat appelé (lecture du code sur le disque, pré-traitement du code et ajout des données à la preuve de Merkle). Dans une situation où l'attaquant n'a besoin que de peu de ressources pour donner beaucoup de travail aux autres nœuds, il y a un risque d'attaques DOS. +Cette limite a été apportée pour empêcher les attaques par déni de service (DOS). Tout appel vers un contrat est relativement peu coûteux en gaz. Cependant, l'impact d'un appel de contrat sur les nœuds Ethereum augmente de manière disproportionnée en fonction de la taille du code du contrat appelé (lecture du code sur le disque, pré-traitement du code et ajout des données à la preuve de Merkle). Dans une situation où l'attaquant n'a besoin que de peu de ressources pour donner beaucoup de travail aux autres nœuds, il y a un risque d'attaques DOS. -À l'origine, le problème était moins préoccupant, car la limite naturelle de la taille des contrats était la limite de gaz par bloc. Bien entendu, un contrat doit être déployé dans une transaction qui contient tout le bytecode du contrat. Si vous n'incluez ensuite que cette seule transaction dans un bloc, vous pourrez utiliser tout le gaz, mais il ne sera pas illimité. Depuis la [mise à niveau de Londres](/ethereum-forks/#london), la limite de gaz de bloc a pu varier entre 15M et 30M unités selon la demande du réseau. +À l'origine, le problème était moins préoccupant, car la limite naturelle de la taille des contrats était la limite de gaz par bloc. Bien entendu, un contrat doit être déployé dans une transaction qui contient tout le bytecode du contrat. Si vous n'incluez ensuite que cette seule transaction dans un bloc, vous pourrez utiliser tout le gaz, mais il ne sera pas illimité. Depuis la [mise à niveau London](/ethereum-forks/#london), la limite de gaz par bloc a pu varier entre 15M et 30M d'unités en fonction de la demande du réseau. -Dans les prochaines lignes, nous examinerons certaines méthodes classées en fonction de leur impact potentiel. Considérez ça comme perdre du poids. La meilleure stratégie pour atteindre son poids cible (dans notre cas, 24 kb) est de se concentrer en premier lieu sur les méthodes à fort impact. Dans la plupart des cas, il suffit de revoir son régime alimentaire pour y parvenir, mais il faut parfois aller un peu plus loin. Ensuite, vous pouvez ajouter à cela un peu d'exercice (impact modéré) ou même des suppléments (impact faible). +Dans les prochaines lignes, nous examinerons certaines méthodes classées en fonction de leur impact potentiel. Considérez cela comme une perte de poids. La meilleure stratégie pour atteindre son poids cible (dans notre cas, 24 ko) est de se concentrer en premier lieu sur les méthodes à fort impact. Dans la plupart des cas, il suffit de revoir son régime alimentaire pour y parvenir, mais il faut parfois aller un peu plus loin. Ensuite, vous pouvez ajouter à cela un peu d'exercice (impact modéré) ou même des suppléments (impact faible). -## Impact important {#big-impact} +## Grand impact {#big-impact} ### Séparez vos contrats {#separate-your-contracts} @@ -37,24 +34,22 @@ Cela devrait toujours être votre première approche. Comment peut-on séparer l ### Bibliothèques {#libraries} -Une façon simple de séparer le code fonctionnel du stockage est d'utiliser une [bibliothèque](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries). Ne déclarez pas les fonctions de la bibliothèque comme internes, sinon elles seront [ajoutées au contrat](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking) directement, lors de la compilation. Mais si vous utilisez des fonctions publiques, celles-ci seront en fait dans un contrat de bibliothèque séparé. Pensez à utiliser « [using for](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for) » pour rendre l'utilisation des bibliothèques plus pratique. +Une façon simple de séparer le code des fonctionnalités du stockage est d'utiliser une [bibliothèque](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries). Ne déclarez pas les fonctions de la bibliothèque comme étant `internal`, car celles-ci seront directement [ajoutées au contrat](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking) lors de la compilation. Mais si vous utilisez des fonctions `public`, celles-ci se trouveront en fait dans un contrat de bibliothèque distinct. Pensez à [utiliser `using for`](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for) pour rendre l'utilisation des bibliothèques plus pratique. -### Proxies {#proxies} +### Proxys {#proxies} -Une méthode plus avancée consiste à utiliser le système de proxy. Les bibliothèques utilisent `DELEGATECALL` en coulisse, qui exécute simplement la fonction d'un autre contrat avec l'état du contrat appelant. Consultez cette [publication de blog](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2) pour en savoir plus sur les systèmes de proxy. Cela vous offrira plus de fonctionnalités, comme permettre les mise à niveau, par exemple, mais ils ajoutent aussi beaucoup de complexité. Je ne les ajouterais pas uniquement pour réduire la taille des contrats, à moins que ce ne soit votre seule option pour une raison quelconque. +Une méthode plus avancée consiste à utiliser le système de proxy. Les bibliothèques utilisent `DELEGATECALL` en coulisse, qui exécute simplement la fonction d'un autre contrat avec l'état du contrat appelant. Consultez [cet article de blog](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2) pour en savoir plus sur les systèmes de proxy. Ils offrent plus de fonctionnalités, par exemple, ils permettent les mises à niveau, mais ils ajoutent aussi beaucoup de complexité. Je ne les ajouterais pas uniquement pour réduire la taille des contrats, à moins que ce ne soit votre seule option pour une raison quelconque. -## Impact modéré {#medium-impact} +## Impact moyen {#medium-impact} -### Supprimez des fonctions {#remove-functions} +### Supprimer des fonctions {#remove-functions} Cela devrait être évident. Les fonctions augmentent considérablement la taille d'un contrat. -- **Externe** : Souvent, nous ajoutons beaucoup de fonctions « view », pour des raisons de commodité. Ce n'est pas vraiment un problème, jusqu'à ce que vous atteigniez la taille limite. Quand cela arrive, vous devriez vraiment penser à supprimer tous les éléments qui ne sont absolument pas essentiels. -- **Interne** : Vous pouvez également supprimer les fonctions internes/privées et mettre le code dans la ligne, à condition qu'elles ne soient appelées qu'une fois. +- **Externe** : Souvent, nous ajoutons de nombreuses fonctions `view` pour des raisons de commodité. Ce n'est pas vraiment un problème, jusqu'à ce que vous atteigniez la taille limite. Quand cela arrive, vous devriez vraiment penser à supprimer tous les éléments qui ne sont absolument pas essentiels. +- **Interne** : Vous pouvez également supprimer les fonctions `internal`/`private` et simplement inliner le code tant que la fonction n'est appelée qu'une seule fois. -### Évitez les variables inutiles {#avoid-additional-variables} - -Un simple changement comme celui-ci : +### Évitez les variables supplémentaires {#avoid-additional-variables} ```solidity function get(uint id) returns (address,address) { @@ -69,15 +64,14 @@ function get(uint id) returns (address,address) { } ``` -représente une différence de **0,28 kb**. Il y a de fortes chances que vous puissiez trouver de nombreuses situations similaires dans vos contrats, et elles peuvent engendrer une augmentation significative du poids total. +Un simple changement comme celui-ci fait une différence de **0,28 ko**. Il y a de fortes chances que vous puissiez trouver de nombreuses situations similaires dans vos contrats, et elles peuvent engendrer une augmentation significative du poids total. -### Abrégez les messages d'erreur {#shorten-error-message} +### Raccourcir les messages d'erreur {#shorten-error-message} -Les longs messages d'annulation et, en particulier, les nombreux messages différents d'annulation peuvent alourdir le contrat. Utilisez plutôt des codes d'erreur courts et décodez-les dans votre contrat. Un long message peut ainsi devenir beaucoup plus court : +De longs messages `revert` et en particulier de nombreux messages `revert` différents peuvent alourdir le contrat. Utilisez plutôt des codes d'erreur courts et décodez-les dans votre contrat. Un long message peut ainsi devenir beaucoup plus court : ```solidity -require(msg.sender == owner, "Only the owner of this contract can call this function"); - +require(msg.sender == owner, "Seul le propriétaire de ce contrat peut appeler cette fonction"); ``` ```solidity @@ -86,7 +80,7 @@ require(msg.sender == owner, "OW1"); ### Utiliser des erreurs personnalisées au lieu de messages d'erreur -Des erreurs personnalisées ont été introduites dans [Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/). Ils sont un excellent moyen de réduire la taille de vos contrats, car ils sont encodés ABI en tant que sélecteurs (comme les fonctions). +Les erreurs personnalisées ont été introduites dans [Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/). C'est un excellent moyen de réduire la taille de vos contrats, car ils sont encodés en ABI en tant que sélecteurs (tout comme le sont les fonctions). ```solidity error Unauthorized(); @@ -96,15 +90,15 @@ if (msg.sender != owner) { } ``` -### Envisagez une valeur d'exécution faible dans l'optimiseur {#consider-a-low-run-value-in-the-optimizer} +### Envisager une faible valeur de `runs` dans l'optimiseur {#consider-a-low-run-value-in-the-optimizer} -Vous pouvez également modifier les paramètres de l'optimiseur. La valeur par défaut à 200 signifie qu'il va essayer d'optimiser le bytecode comme si une fonction était appelée 200 fois. Si vous la définissez à 1, vous demandez simplement à l'optimiseur d'optimiser dans le cas où chaque fonction n'est exécutée qu'une seule fois. Une fonction optimisée pour une seule exécution signifie qu'elle est optimisée pour le déploiement lui-même. Sachez que **cela augmente les [coûts en gaz](/developers/docs/gas/) pour l'exécution des fonctions**, donc vous ne voudrez peut-être pas le faire. +Vous pouvez également modifier les paramètres de l'optimiseur. La valeur par défaut de 200 signifie qu'il essaie d'optimiser le bytecode comme si une fonction était appelée 200 fois. Si vous la changez pour 1, vous indiquez simplement à l'optimiseur d'optimiser pour le cas où chaque fonction n'est exécutée qu'une seule fois. Une fonction optimisée pour une seule exécution signifie qu'elle est optimisée pour le déploiement lui-même. Sachez que **cela augmente les [coûts en gaz](/developers/docs/gas/) pour l'exécution des fonctions**, donc vous ne voudrez peut-être pas le faire. -## Impact faible {#small-impact} +## Faible impact {#small-impact} -### Évitez de passer des structures aux fonctions {#avoid-passing-structs-to-functions} +### Évitez de passer des `structs` aux fonctions {#avoid-passing-structs-to-functions} -Si vous utilisez [ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2), il peut être utile de ne pas passer de structures à une fonction. Au lieu de passer le paramètre comme structure... +Si vous utilisez l'[ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2), il peut être utile de ne pas passer de `structs` à une fonction. Au lieu de passer le paramètre en tant que `struct`, passez les paramètres requis directement. Dans cet exemple, nous avons économisé **0,1 ko** de plus. ```solidity function get(uint id) returns (address,address) { @@ -126,14 +120,12 @@ function _get(address addr1, address addr2) private view returns(address,address } ``` -... passez les paramètres requis directement. Dans l'exemple ci-dessus, nous avons économisé **0,1 kb** de plus. - -### Spécifiez des visibilités appropriées pour vos fonctions et variables {#declare-correct-visibility-for-functions-and-variables} +### Déclarer la bonne visibilité pour les fonctions et les variables {#declare-correct-visibility-for-functions-and-variables} -- Des fonctions ou des variables uniquement appelées de l'extérieur ? Déclarez-les `external` au lieu de `public`. -- Des fonctions ou des variables uniquement appelées au sein du contrat ? Déclarez-les `private` ou `internal` au lieu de `public`. +- Des fonctions ou des variables uniquement appelées de l'extérieur ? Déclarez-les comme `external` au lieu de `public`. +- Des fonctions ou des variables uniquement appelées au sein du contrat ? Déclarez-les comme `private` ou `internal` au lieu de `public`. -### Retirez les modificateurs {#remove-modifiers} +### Supprimer les modificateurs {#remove-modifiers} Les modificateurs, surtout lorsqu'ils sont utilisés de manière intensive, peuvent avoir un impact significatif sur la taille du contrat. Envisagez de les supprimer et d'utiliser plutôt des fonctions. diff --git a/public/content/translations/fr/developers/tutorials/eip-1271-smart-contract-signatures/index.md b/public/content/translations/fr/developers/tutorials/eip-1271-smart-contract-signatures/index.md index 0421276c605..5164794909b 100644 --- a/public/content/translations/fr/developers/tutorials/eip-1271-smart-contract-signatures/index.md +++ b/public/content/translations/fr/developers/tutorials/eip-1271-smart-contract-signatures/index.md @@ -1,20 +1,22 @@ --- title: "EIP-1271 : Signature et vérification des signatures de contrats intelligents" -description: Un aperçu de la génération et de la vérification de signatures de contrat intelligent avec l'EIP-1271. Nous examinons également la mise en œuvre de l'EIP-1271 utilisée dans Safe (anciennement Gnosis Safe) pour fournir un exemple concret aux développeurs de contrats intelligents sur lequel s'appuyer. +description: "Un aperçu de la génération et de la vérification de signatures de contrat intelligent avec l'EIP-1271. Nous examinons également la mise en œuvre de l'EIP-1271 utilisée dans Safe (anciennement Gnosis Safe) pour fournir un exemple concret aux développeurs de contrats intelligents sur lequel s'appuyer." author: Nathan H. Leung lang: fr tags: - - "eip-1271" - - "contrats intelligents" - - "vérification" - - "Signature" + [ + "eip-1271", + "contrats intelligents", + "vérification", + "Signature" + ] skill: intermediate published: 2023-01-12 --- La norme [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) permet aux contrats intelligents de vérifier les signatures. -Dans ce tutoriel, nous donnons un aperçu des signatures numériques, de l'historique de l'EIP-1271 et de la mise en œuvre spécifique de l'EIP-1271 utilisée par [Safe](https://safe.global/) (anciennement Gnosis Safe). L'ensemble peut servir de point de départ à la mise en œuvre de la norme EIP-1271 dans vos propres contrats. +Dans ce tutoriel, nous donnons un aperçu des signatures numériques, du contexte de l'EIP-1271 et de la mise en œuvre spécifique de l'EIP-1271 utilisée par [Safe](https://safe.global/) (anciennement Gnosis Safe). L'ensemble peut servir de point de départ à la mise en œuvre de la norme EIP-1271 dans vos propres contrats. ## Qu'est-ce qu'une signature ? @@ -36,11 +38,11 @@ De même, une signature numérique ne signifie rien sans un message associé ! Pour créer une signature numérique à utiliser sur les blockchains basées sur Ethereum, vous avez généralement besoin d'une clé privée secrète que personne d'autre ne connaît. C'est ce qui fait que votre signature vous appartient (personne d'autre ne peut créer la même signature sans connaître la clé secrète). -Votre compte Ethereum (c'est-à-dire votre compte externe/EOA) est associé à une clé privée, et c'est cette clé privée qui est généralement utilisée lorsqu'un site web ou une application vous demande une signature (par exemple, pour « Se connecter avec Ethereum »). +Votre compte Ethereum (c.-à-d. votre compte détenu en externe/EOA) est associé à une clé privée, et c'est cette clé privée qui est généralement utilisée lorsqu'un site Web ou une dapp vous demande une signature (p. ex. pour « Se connecter avec Ethereum »). -Une application peut [vérifier une signature](https://docs.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum) que vous créez à l'aide d'une bibliothèque tierce telle que ethers.js [sans connaître votre clé privée](https://en.wikipedia.org/wiki/Public-key_cryptography) et être certaine que _vous_ êtes celui/celle qui a créé la signature. +Une application peut [vérifier une signature](https://www.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum) que vous créez à l'aide d'une bibliothèque tierce comme ethers.js [sans connaître votre clé privée](https://en.wikipedia.org/wiki/Public-key_cryptography) et avoir la certitude que c'est _vous_ qui avez créé la signature. -> En fait, comme les signatures numériques EOA utilisent la cryptographie à clé publique, elles peuvent être générées et vérifiées **hors chaîne** ! C'est comme cela que fonctionne les votes DAO sans gaz - au lieu de soumettre les votes sur la chaîne, les signatures numériques peuvent être créées et vérifiées hors chaîne à l'aide de bibliothèques cryptographiques. +> En fait, parce que les signatures numériques EOA utilisent la cryptographie à clé publique, elles peuvent être générées et vérifiées **hors chaîne** ! C'est ainsi que fonctionne le vote DAO sans gaz — au lieu de soumettre des votes en chaîne, les signatures numériques peuvent être créées et vérifiées hors chaîne à l'aide de bibliothèques cryptographiques. Alors que les comptes EOA disposent d'une clé privée, les comptes de contrats intelligents ne disposent d'aucune forme de clé privée ou secrète (de sorte que la fonction « Se connecter avec Ethereum », etc. ne peut pas fonctionner nativement avec les comptes de contrats intelligents). @@ -50,17 +52,17 @@ Le problème que l'EIP-1271 cherche à résoudre : comment savoir si la signatur Les contrats intelligents ne disposent pas de clés privées pouvant être utilisées pour signer des messages. Alors, comment savoir si une signature est authentique ? -Eh bien, une idée serait tout simplement de _demander_ au contrat intelligent si une signature est authentique ! +Eh bien, une idée est que nous pouvons simplement _demander_ au contrat intelligent si une signature est authentique ! L'EIP-1271 normalise l'idée de « demander » à un contrat intelligent si une signature donnée est valide. -Un contrat qui implémente l'EIP-1271 doit avoir une fonction appelée `isValidSignature` qui prend en compte un message et une signature. Le contrat peut alors exécuter une logique de validation (la spécification n'impose rien de spécifique ici) et renvoyer une valeur indiquant si la signature est valide ou non. +Un contrat qui implémente l'EIP-1271 doit avoir une fonction appelée `isValidSignature` qui prend en entrée un message et une signature. Le contrat peut alors exécuter une logique de validation (la spécification n'impose rien de spécifique ici) et renvoyer une valeur indiquant si la signature est valide ou non. Si `isValidSignature` renvoie un résultat valide, c'est en gros le contrat qui dit « oui, j'approuve cette signature + ce message ! » ### Interface -Voici l'interface exacte dans la spécification EIP-1271 (nous parlerons du paramètre `_hash` plus loin, mais pour l'instant, considérez-le comme le message qui est vérifié) : +Voici l'interface exacte dans la spécification EIP-1271 (nous parlerons du paramètre `_hash` ci-dessous, mais pour l'instant, considérez-le comme le message en cours de vérification) : ```jsx pragma solidity ^0.5.0; @@ -71,13 +73,13 @@ contract ERC1271 { bytes4 constant internal MAGICVALUE = 0x1626ba7e; /** - * @dev Should return whether the signature provided is valid for the provided hash - * @param _hash Hash of the data to be signed - * @param _signature Signature byte array associated with _hash + * @dev Devrait retourner si la signature fournie est valide pour le hachage fourni + * @param _hash Hachage des données à signer + * @param _signature Tableau d'octets de la signature associé à _hash * - * MUST return the bytes4 magic value 0x1626ba7e when function passes. - * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5) - * MUST allow external calls + * DOIT retourner la valeur magique bytes4 0x1626ba7e lorsque la fonction réussit. + * NE DOIT PAS modifier l'état (en utilisant STATICCALL pour solc < 0.5, modificateur view pour solc > 0.5) + * DOIT autoriser les appels externes */ function isValidSignature( bytes32 _hash, @@ -90,28 +92,28 @@ contract ERC1271 { ## Exemple d'implémentation EIP-1271 : Safe -Les contrats peuvent implémenter `isValidSignature` de plusieurs façons - la spécification seule ne précise pas l'implémentation exacte. +Les contrats peuvent implémenter `isValidSignature` de nombreuses manières — la spécification elle-même ne dit pas grand-chose sur l'implémentation exacte. Un contrat significatif qui met en œuvre l'EIP-1271 est Safe (anciennement Gnosis Safe). -Dans le code de Safe, la fonction `isValidSignature` [est implémentée](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol) de manière à ce que les signatures puissent être créées et vérifiées de [deux manières](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support) : +Dans le code de Safe, `isValidSignature` [est implémenté](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol) de sorte que les signatures peuvent être créées et vérifiées de [deux manières](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support) : -1. Messages on-chain - 1. Création : un propriétaire du coffre-fort crée une nouvelle transaction sécurisée pour « signer » un message, en transmettant le message sous forme de données dans la transaction. Une fois que suffisamment de propriétaires ont signé la transaction pour atteindre le seuil multisig, la transaction est diffusée et exécutée. Dans la transaction, une fonction « safe » est invoquée afin d'ajouter le message à une liste de messages « approuvés ». - 2. Vérification : appelez `isValidSignature` sur le contrat Safe, et envoyez le message à vérifier en tant que paramètre du message et [une valeur vide pour le paramètre de signature](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (c'est-à-dire `0x`). Le Safe verra que le paramètre de signature est vide et au lieu de vérifier cryptographiquement la signature, il saura simplement aller de l'avant et vérifier si le message figure sur la liste des messages « approuvés ». +1. Messages en chaîne + 1. Création : un propriétaire du coffre-fort crée une nouvelle transaction sécurisée pour « signer » un message, en transmettant le message sous forme de données dans la transaction. Une fois que suffisamment de propriétaires ont signé la transaction pour atteindre le seuil multisig, la transaction est diffusée et exécutée. Dans la transaction, il y a une fonction de Safe appelée (`signMessage(bytes calldata _data)`) qui ajoute le message à une liste de messages « approuvés ». + 2. Vérification : appelez `isValidSignature` sur le contrat Safe, et passez le message à vérifier comme paramètre de message et [une valeur vide pour le paramètre de signature](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (c.-à-d., `0x`). Le Safe verra que le paramètre de signature est vide et au lieu de vérifier cryptographiquement la signature, il saura simplement aller de l'avant et vérifier si le message figure sur la liste des messages « approuvés ». 2. Messages hors chaîne : - 1. Création : un propriétaire de coffre-fort crée un message hors chaîne, puis demande à d'autres propriétaires de coffre-fort de signer le message chacun individuellement jusqu'à ce qu'il y ait suffisamment de signatures pour dépasser le seuil d'approbation multisig. + 1. Création : un propriétaire de Safe crée un message hors chaîne, puis demande à d'autres propriétaires de Safe de signer le message chacun individuellement jusqu'à ce qu'il y ait suffisamment de signatures pour dépasser le seuil d'approbation multisig. 2. Vérification : appelez `isValidSignature`. Dans le paramètre du message, envoyez le message à vérifier. Dans le paramètre de signature, transmettez les signatures individuelles de chaque propriétaire de coffre-fort, toutes concaténées ensemble, dos à dos. Le Safe vérifiera qu'il y a suffisamment de signatures pour atteindre le seuil **et** que chaque signature est valide. Si c'est le cas, il renverra une valeur indiquant une vérification de signature réussie. -## Qu'est-ce que le paramètre `_hash` ? Pourquoi ne pas transmettre le message dans son intégralité ? +## Qu'est-ce que le paramètre `_hash` exactement ? Pourquoi ne pas transmettre le message dans son intégralité ? -Vous avez peut-être remarqué que la fonction `isValidSignature` dans l'[interface de l'EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) ne prend pas le message lui-même, mais plutôt un paramètre `_hash`. Ce que cela signifie, c'est qu'au lieu de passer le message complet de longueur arbitraire à `isValidSignature`, nous passons plutôt un hash de 32 octets du message (généralement keccak256). +Vous avez peut-être remarqué que la fonction `isValidSignature` dans [l'interface EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) ne prend pas en entrée le message lui-même, mais plutôt un paramètre `_hash`. Cela signifie qu'au lieu de passer le message complet de longueur arbitraire à `isValidSignature`, nous passons un hachage de 32 octets du message (généralement keccak256). -Chaque octet de calldata - c'est-à-dire les données des paramètres de fonction passées à une fonction de contrat intelligent - [coûte 16 gaz (4 gaz si l'octet est zéro)](https://eips.ethereum.org/EIPS/eip-2028), cela peut donc économiser beaucoup de gaz si un message est long. +Chaque octet de calldata — c.-à-d. les données de paramètre de fonction passées à une fonction de contrat intelligent — [coûte 16 gaz (4 gaz si l'octet est un zéro)](https://eips.ethereum.org/EIPS/eip-2028), ce qui peut permettre d'économiser beaucoup de gaz si un message est long. ### Spécifications précédentes de l'EIP-1271 -Il existe des spécifications de l'EIP-1271 dans la nature qui ont une fonction `isValidSignature` avec un premier paramètre de type `bytes` (longueur arbitraire, au lieu d'une longueur fixe `bytes32`) et un nom de paramètre `message`. C'est une [version plus ancienne](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206) de l'EIP-1271. +Il existe dans la nature des spécifications EIP-1271 qui ont une fonction `isValidSignature` avec un premier paramètre de type `bytes` (longueur arbitraire, au lieu d'une longueur fixe `bytes32`) et le nom de paramètre `message`. Il s'agit d'une [ancienne version](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206) de la norme EIP-1271. ## Comment implémenter l'EIP-1271 dans mes propres contrats ? @@ -124,4 +126,4 @@ En fin de compte, c'est à vous de décider en tant que développeur de contrat ## Conclusion -[L'EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) est une norme polyvalente qui permet aux contrats intelligents de vérifier les signatures. Cela ouvre la voie à des contrats intelligents pour qu'ils agissent davantage comme des EOA - par exemple, en offrant un moyen de faire fonctionner la fonction « Se connecter avec Ethereum » avec les contrats intelligents — et cela peut être implémenté de plusieurs façons (Safe offre une implémentation intéressante et originale à prendre en compte). +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) est une norme polyvalente qui permet aux contrats intelligents de vérifier les signatures. Cela ouvre la voie à des contrats intelligents pour qu'ils agissent davantage comme des EOA - par exemple, en offrant un moyen de faire fonctionner la fonction « Se connecter avec Ethereum » avec les contrats intelligents — et cela peut être implémenté de plusieurs façons (Safe offre une implémentation intéressante et originale à prendre en compte). diff --git a/public/content/translations/fr/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/translations/fr/developers/tutorials/erc-721-vyper-annotated-code/index.md index 69190ba7dbd..2fc9e4c2c93 100644 --- a/public/content/translations/fr/developers/tutorials/erc-721-vyper-annotated-code/index.md +++ b/public/content/translations/fr/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -3,29 +3,29 @@ title: "Découvrir le contrat Vyper ERC-721" description: Le contrat ERC-721 de Ryuya Nakamura et son fonctionnement author: Ori Pomerantz lang: fr -tags: - - "vyper" - - "erc-721" - - "python" +tags: [ "vyper", "erc-721", "python" ] skill: beginner published: 2021-04-01 --- ## Introduction {#introduction} -[ERC-721](/developers/docs/standards/tokens/erc-721/) est une norme utilisée pour garantir la propriété des jetons non fongibles (NFT). Les jetons [ERC-20](/developers/docs/standards/tokens/erc-20/) se comportent comme une monnaie, car il n'y a aucune différence entre chacun d'eux. À l'inverse, les jetons ERC-721 ont été conçus pour des actifs qui sont similaires mais pas identiques, comme par exemple différents [dessins de chats](https://www.cryptokitties.co/) ou différents titres de propriété de biens immobiliers. +La norme [ERC-721](/developers/docs/standards/tokens/erc-721/) est utilisée pour détenir la propriété des jetons non fongibles (NFT). +Les jetons [ERC-20](/developers/docs/standards/tokens/erc-20/) se comportent comme un produit de base, car il n'y a aucune différence entre les jetons individuels. +En revanche, les jetons ERC-721 sont conçus pour des actifs similaires mais non identiques, tels que différents [dessins animés de chats](https://www.cryptokitties.co/) ou des titres de propriété pour différents biens immobiliers. -Dans cet article, nous allons décortiquer le [contrat ERC-721 de Ryuya Nakamura](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy). Ce contrat a été écrit en [Vyper](https://vyper.readthedocs.io/en/latest/index.html), un langage de contrat similaire à Python, conçu pour rendre plus difficile l'écriture de code non sécurisé que ce n'est le cas dans Solidity. +Dans cet article, nous analyserons le [contrat ERC-721 de Ryuya Nakamura](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy). +Ce contrat est écrit en [Vyper](https://vyper.readthedocs.io/en/latest/index.html), un langage de contrat de type Python conçu pour rendre plus difficile l'écriture de code non sécurisé qu'en Solidity. ## Le contrat {#contract} ```python -# @dev Implementation of ERC-721 non-fungible token standard. +# @dev Implémentation de la norme de jeton non fongible ERC-721. # @author Ryuya Nakamura (@nrryuya) -# Modified from: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy +# Modifié à partir de : https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy ``` -Tout comme avec Python, les commentaires Vyper commencent par une empreinte numérique (`#`) et continuent jusqu'au bout de la ligne. Les commentaires qui comportent `@` sont compris par [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) afin de produire une documentation compréhensible pour l'être humain. +Les commentaires en Vyper, comme en Python, commencent par un dièse (`#`) et se poursuivent jusqu'à la fin de la ligne. Les commentaires qui incluent `@` sont utilisés par [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) pour produire une documentation lisible par l'homme. ```python from vyper.interfaces import ERC721 @@ -33,159 +33,162 @@ from vyper.interfaces import ERC721 implements: ERC721 ``` -L'interface ERC-721 est intégrée au langage Vyper. [Vous pouvez lire sa définition en code Python ici](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py). La définition de l'interface est écrite en Python plutôt qu'en Vyper. En effet, les interfaces ne sont pas utilisées seulement au sein de la blockchain, mais aussi lors de l'envoi d'une transaction vers la blockchain depuis un client externe, qui peut avoir été écrit en Python. +L'interface ERC-721 est intégrée au langage Vyper. +[Vous pouvez voir la définition du code ici](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py). +La définition de l'interface est écrite en Python plutôt qu'en Vyper, car les interfaces ne sont pas seulement utilisées au sein de la blockchain, mais aussi lors de l'envoi d'une transaction à la blockchain depuis un client externe, qui peut être écrit en Python. La première ligne importe l'interface, et la deuxième spécifie que nous l'implémentons ici. ### L'interface ERC721Receiver {#receiver-interface} ```python -# Interface for the contract called by safeTransferFrom() +# Interface pour le contrat appelé par safeTransferFrom() interface ERC721Receiver: def onERC721Received( ``` -ERC-721 autorise deux types de transfert : +L'ERC-721 prend en charge deux types de transfert : -- `transferFrom`, qui permet à l'expéditeur de spécifier n'importe quelle adresse de destination et fait reposer la responsabilité du transfert sur l'expéditeur. Cela signifie que vous pouvez effectuer un transfert vers une adresse erronée, auquel cas le NFT sera définitivement perdu. -- `safeTransferFrom`, qui vérifie si l'adresse de destination est un contrat. Si c'est le cas, le contrat ERC-721 demande au contrat destinataire s'il accepte de recevoir le NFT. +- `transferFrom`, qui permet à l'expéditeur de spécifier n'importe quelle adresse de destination et qui lui attribue la responsabilité du transfert. Cela signifie que vous pouvez effectuer un transfert vers une adresse invalide, auquel cas le NFT est perdu à jamais. +- `safeTransferFrom`, qui vérifie si l'adresse de destination est un contrat. Si c'est le cas, le contrat ERC-721 demande au contrat destinataire s'il souhaite recevoir le NFT. -Pour répondre aux requêtes de `safeTransferFrom`, le contrat destinataire doit implémenter `ERC721Receiver`. +Pour répondre aux requêtes `safeTransferFrom`, un contrat destinataire doit implémenter `ERC721Receiver`. ```python _operator: address, _from: address, ``` -L'adresse `_from` est le propriétaire actuel du jeton. L'adresse `_operator` est celle qui a demandé le transfert (les deux peuvent ne pas être identiques, en raison des quotas). +L'adresse `_from` est le propriétaire actuel du jeton. L'adresse `_operator` est celle qui a demandé le transfert (ces deux adresses peuvent ne pas être les mêmes, en raison des autorisations). ```python _tokenId: uint256, ``` -Les identifiants des jetons ERC-721 comportent 256 bits. Il sont généralement créés par le hachage de la description qui représente le jeton. +Les ID de jetons ERC-721 sont de 256 bits. Généralement, ils sont créés en effectuant un hachage de la description de ce que le jeton représente. ```python _data: Bytes[1024] ``` -La requête peut comporter jusqu'à 1024 octets de données utilisateur. +La requête peut contenir jusqu'à 1024 octets de données utilisateur. ```python ) -> bytes32: view ``` -Pour éviter les cas où un contrat accepte accidentellement un transfert, la valeur de retour n'est pas un booléen, mais 256 bits avec une valeur spécifique. +Pour éviter les cas où un contrat accepte accidentellement un transfert, la valeur de retour n'est pas un booléen, mais une valeur de 256 bits avec une valeur spécifique. -Cette fonction est de type `view`, ce qui signifie qu'elle peut consulter l'état de la blockchain, mais pas le modifier. +Cette fonction est une `view`, ce qui signifie qu'elle peut lire l'état de la blockchain, mais pas le modifier. -### Évènements {#events} +### Événements {#events} -Les [évènements](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e) sont émis pour informer les utilisateurs et les serveurs extérieurs à la blockchain des évènements. Notez que le contenu des évènements n'est pas accessible aux contrats sur la blockchain. +Des [événements](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e) sont émis pour informer les utilisateurs et les serveurs en dehors de la blockchain. Notez que le contenu des événements n'est pas disponible pour les contrats sur la blockchain. ```python -# @dev Emits when ownership of any NFT changes by any mechanism. This event emits when NFTs are -# created (`from` == 0) and destroyed (`to` == 0). Exception: during contract creation, any -# number of NFTs may be created and assigned without emitting Transfer. At the time of any -# transfer, the approved address for that NFT (if any) is reset to none. -# @param _from Sender of NFT (if address is zero address it indicates token creation). -# @param _to Receiver of NFT (if address is zero address it indicates token destruction). -# @param _tokenId The NFT that got transferred. +# @dev Émet lorsque la propriété d'un NFT change par n'importe quel mécanisme. Cet événement est émis lorsque des NFT sont +# créés (`from` == 0) et détruits (`to` == 0). Exception : lors de la création du contrat, un +# nombre quelconque de NFT peut être créé et attribué sans émettre de Transfer. Au moment d'un +# transfert, l'adresse approuvée pour ce NFT (le cas échéant) est réinitialisée à aucune. +# @param _from Expéditeur du NFT (si l'adresse est l'adresse zéro, cela indique la création du jeton). +# @param _to Destinataire du NFT (si l'adresse est l'adresse zéro, cela indique la destruction du jeton). +# @param _tokenId Le NFT qui a été transféré. event Transfer: sender: indexed(address) receiver: indexed(address) tokenId: indexed(uint256) ``` -On retrouve des similitudes avec l'évènement Transfer ERC-20, à l'exception du fait que nous fournissons un `tokenId` au lieu d'un montant. Personne n'est propriétaire de l'adresse zéro, donc par convention, on l'utilise pour indiquer la création et la destruction des jetons. +C'est similaire à l'événement Transfer de l'ERC-20, sauf que nous déclarons un `tokenId` au lieu d'un montant. +Personne ne possède l'adresse zéro, donc par convention, nous l'utilisons pour signaler la création et la destruction des jetons. ```python -# @dev This emits when the approved address for an NFT is changed or reaffirmed. The zero -# address indicates there is no approved address. When a Transfer event emits, this also -# indicates that the approved address for that NFT (if any) is reset to none. -# @param _owner Owner of NFT. -# @param _approved Address that we are approving. -# @param _tokenId NFT which we are approving. +# @dev Émet lorsque l'adresse approuvée pour un NFT est modifiée ou reconfirmée. L'adresse +# zéro indique qu'il n'y a pas d'adresse approuvée. Lorsqu'un événement Transfer est émis, cela +# indique également que l'adresse approuvée pour ce NFT (le cas échéant) est réinitialisée à aucune. +# @param _owner Propriétaire du NFT. +# @param _approved Adresse que nous approuvons. +# @param _tokenId NFT que nous approuvons. event Approval: owner: indexed(address) approved: indexed(address) tokenId: indexed(uint256) ``` -Un évènement d'approbation ERC-721 est comparable à une autorisation ERC-20. Une adresse spécifique est autorisée à transférer un jeton spécifique. Cela donne un mécanisme permettant aux contrats de répondre lorsqu'ils acceptent un jeton. Les contrats ne peuvent pas écouter les évènements, donc si vous leur transférez simplement le jeton, ils n'en seront pas « informés ». De cette façon, le propriétaire envoie d'abord un évènement d'approbation, puis envoie une demande au contrat : « J'ai autorisé le transfert du jeton X, veuillez ... ». +Une approbation ERC-721 est similaire à une autorisation ERC-20. Une adresse spécifique est autorisée à transférer un jeton spécifique. Cela fournit un mécanisme permettant aux contrats de répondre lorsqu'ils acceptent un jeton. Les contrats ne peuvent pas écouter les événements, donc si vous leur transférez simplement le jeton, ils n'en seront pas « informés ». De cette façon, le propriétaire soumet d'abord une approbation, puis envoie une demande au contrat : « Je vous ai autorisé à transférer le jeton X, veuillez le faire... ». -Il s'agit d'un choix de conception visant à rendre la norme ERC-721 similaire à la norme ERC-20. Les jetons ERC-721 n'étant pas fongibles, un contrat peut aussi déterminer qu'il a reçu un jeton spécifique en regardant la propriété du jeton. +Il s'agit d'un choix de conception visant à rendre la norme ERC-721 similaire à la norme ERC-20. Comme les jetons ERC-721 ne sont pas fongibles, un contrat peut également identifier qu'il a reçu un jeton spécifique en consultant la propriété du jeton. ```python -# @dev This emits when an operator is enabled or disabled for an owner. The operator can manage -# all NFTs of the owner. -# @param _owner Owner of NFT. -# @param _operator Address to which we are setting operator rights. -# @param _approved Status of operator rights(true if operator rights are given and false if -# revoked). +# @dev Émet lorsqu'un opérateur est activé ou désactivé pour un propriétaire. L'opérateur peut gérer +# tous les NFT du propriétaire. +# @param _owner Propriétaire du NFT. +# @param _operator Adresse à laquelle nous accordons les droits d'opérateur. +# @param _approved Statut des droits d'opérateur (true si les droits d'opérateur sont accordés et false si +# révoqués). event ApprovalForAll: owner: indexed(address) operator: indexed(address) approved: bool ``` -Il est parfois utile de disposer d'un _opérateur_ qui peut gérer tous les jetons d'un compte d'un type spécifique (ceux qui sont gérés par un contrat spécifique), à la manière d'une procuration. Par exemple, je pourrais vouloir donner ce rôle à un contrat qui vérifie si je ne l'ai pas contacté pendant six mois et qui, le cas échéant, distribuerait mes biens à mes héritiers (si l'un d'entre eux le demande, en effet, les contrats ne peuvent rien faire sans être appelés par une transaction). Avec ERC-20, nous avons simplement à allouer un quota élevé à un contrat d'héritage, mais cela ne fonctionne pas avec ERC-721, car les jetons ne sont pas fongibles. C'est l'équivalent. +Il est parfois utile de disposer d'un _opérateur_ qui peut gérer tous les jetons d'un compte d'un type spécifique (ceux qui sont gérés par un contrat spécifique), à la manière d'une procuration. Par exemple, je pourrais vouloir donner un tel pouvoir à un contrat qui vérifie si je ne l'ai pas contacté depuis six mois et, si c'est le cas, distribue mes actifs à mes héritiers (si l'un d'eux le demande, car les contrats ne peuvent rien faire sans être appelés par une transaction). Avec l'ERC-20, nous pouvons simplement donner une autorisation élevée à un contrat d'héritage, mais cela ne fonctionne pas pour l'ERC-721 car les jetons ne sont pas fongibles. C'est l'équivalent. -La valeur `approved` nous indique si l'évènement concerne une approbation ou bien le retrait d'une approbation. +La valeur `approved` nous indique si l'événement concerne une approbation ou le retrait d'une approbation. ### Variables d'état {#state-vars} -Ces variables contiennent l'état actuel des jetons : lesquels sont disponibles et qui les possède. La plupart d'entre elles sont des objets de type `HashMap`, [une mise en correspondance (mapping) unidirectionnelle entre deux types](https://vyper.readthedocs.io/en/latest/types.html#mappings). +Ces variables contiennent l'état actuel des jetons : lesquels sont disponibles et qui les possède. La plupart d'entre elles sont des objets `HashMap`, des [mappages unidirectionnels qui existent entre deux types](https://vyper.readthedocs.io/en/latest/types.html#mappings). ```python -# @dev Mapping from NFT ID to the address that owns it. +# @dev Mappage de l'ID du NFT à l'adresse qui le possède. idToOwner: HashMap[uint256, address] -# @dev Mapping from NFT ID to approved address. +# @dev Mappage de l'ID du NFT à l'adresse approuvée. idToApprovals: HashMap[uint256, address] ``` -Les identités des utilisateurs et les contrats sur Ethereum sont représentés par des adresses de 160 bits. Les deux variables ci-dessus mettent respectivement en correspondance les identifiants des jetons à leur propriétaire, et aux personnes autorisées à les transférer (au maximum un jeton pour chacun). Sur Ethereum, les données non initialisées sont toujours égales à zéro, donc s'il n'y a pas de propriétaire ou de transféreur approuvé, la valeur de ce jeton sera zéro. +Les identités des utilisateurs et des contrats dans Ethereum sont représentées par des adresses de 160 bits. Ces deux variables mappent les ID des jetons à leurs propriétaires et à ceux approuvés pour les transférer (au maximum un pour chaque). Dans Ethereum, les données non initialisées sont toujours nulles, donc s'il n'y a pas de propriétaire ou de transféreur approuvé, la valeur de ce jeton est nulle. ```python -# @dev Mapping from owner address to count of his tokens. +# @dev Mappage de l'adresse du propriétaire au nombre de ses jetons. ownerToNFTokenCount: HashMap[address, uint256] ``` -Cette variable contient le nombre de jetons pour chaque propriétaire. Il n'y a pas de correspondance entre les propriétaires et les jetons, donc la seule manière d'identifier les jetons qu'un propriétaire spécifique possède est de regarder dans l'historique des évènements de la blockchain et d'y trouver les évènements `Transfer` associés à ce dernier. Cette variable peut être utilisée pour déterminer quand nous avons tous les NFT et que nous n'avons pas besoin d'attendre plus longtemps. +Cette variable contient le nombre de jetons pour chaque propriétaire. Il n'y a pas de mappage des propriétaires vers les jetons, donc la seule façon d'identifier les jetons qu'un propriétaire spécifique possède est de consulter l'historique des événements de la blockchain et de voir les événements `Transfer` appropriés. Nous pouvons utiliser cette variable pour savoir quand nous avons tous les NFT et que nous n'avons pas besoin de chercher plus loin dans le temps. -Veuillez noter que cet algorithme ne fonctionne qu'avec les interfaces utilisateurs et les serveurs externes. Le code qui s'exécute sur la blockchain elle-même ne peut pas accéder aux évènements passés. +Notez que cet algorithme ne fonctionne que pour les interfaces utilisateur et les serveurs externes. Le code s'exécutant sur la blockchain elle-même ne peut pas lire les événements passés. ```python -# @dev Mapping from owner address to mapping of operator addresses. +# @dev Mappage de l'adresse du propriétaire au mappage des adresses des opérateurs. ownerToOperators: HashMap[address, HashMap[address, bool]] ``` -Un compte peut avoir plus d'un opérateur. Une simple `HashMap` est insuffisante pour tous les stocker, parce que chaque clé correspond à une seule valeur. À la place, vous pouvez utiliser `HashMap[address, bool]` en tant que valeur. Par défaut, la valeur de chaque adresse est `False`, ce qui signifie qu'il ne s'agit pas d'un opérateur. Vous pouvez changer les valeurs à `True` si nécessaire. +Un compte peut avoir plus d'un opérateur. Un `HashMap` simple est insuffisant pour en garder la trace, car chaque clé mène à une seule valeur. À la place, vous pouvez utiliser `HashMap[address, bool]` comme valeur. Par défaut, la valeur de chaque adresse est `False`, ce qui signifie qu'il ne s'agit pas d'un opérateur. Vous pouvez définir les valeurs à `True` si nécessaire. ```python -# @dev Address of minter, who can mint a token +# @dev Adresse du minter, qui peut frapper un jeton minter: address ``` -Les nouveaux jetons doivent être créés d'une manière ou d'une autre. Dans ce contrat, une seule entité est autorisée à le faire, le `minter`. Cela devrait suffire dans le cas d'un jeu, par exemple. Dans d'autres situations, il se pourrait que vous ayez besoin de créer une stratégie commerciale plus complexe. +Les nouveaux jetons doivent être créés d'une manière ou d'une autre. Dans ce contrat, il n'y a qu'une seule entité autorisée à le faire, le `minter`. Ceci est probablement suffisant pour un jeu, par exemple. À d'autres fins, il pourrait être nécessaire de créer une logique métier plus compliquée. ```python -# @dev Mapping of interface id to bool about whether or not it's supported +# @dev Mappage de l'ID de l'interface à un booléen indiquant si elle est prise en charge ou non supportedInterfaces: HashMap[bytes32, bool] -# @dev ERC165 interface ID of ERC165 +# @dev ID d'interface ERC165 de ERC165 ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7 -# @dev ERC165 interface ID of ERC721 +# @dev ID d'interface ERC165 de ERC721 ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd ``` -[ERC-165](https://eips.ethereum.org/EIPS/eip-165) définit un mécanisme permettant à un contrat de préciser comment les applications peuvent communiquer avec ce dernier, auquel les normes ERC se conforment. Dans cet exemple, le contrat se conforme aux normes ERC-165 et ERC-721. +L'[ERC-165](https://eips.ethereum.org/EIPS/eip-165) spécifie un mécanisme permettant à un contrat de divulguer la manière dont les applications peuvent communiquer avec lui et à quelles normes ERC il se conforme. Dans ce cas, le contrat est conforme aux normes ERC-165 et ERC-721. ### Fonctions {#functions} -Ce sont les fonctions qui mettent véritablement ERC-721 en œuvre. +Ce sont les fonctions qui implémentent réellement l'ERC-721. #### Constructeur {#constructor} @@ -194,15 +197,15 @@ Ce sont les fonctions qui mettent véritablement ERC-721 en œuvre. def __init__(): ``` -En Vyper, tout comme en Python, la fonction constructeur est appelée `__init__`. +En Vyper, comme en Python, la fonction constructeur est appelée `__init__`. ```python """ - @dev Contract constructor. + @dev Constructeur de contrat. """ ``` -En Python, et en Vyper, vous pouvez aussi écrire des commentaires en spécifiant une chaîne de caractères multi-lignes (qui doit commencer et se terminer par `"""`), et ne l'utiliser en aucune façon. Les commentaires prennent également [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) en charge. +En Python et en Vyper, vous pouvez également créer un commentaire en spécifiant une chaîne multiligne (qui commence et se termine par `"""`), sans l'utiliser d'aucune façon. Ces commentaires peuvent également inclure [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html). ```python self.supportedInterfaces[ERC165_INTERFACE_ID] = True @@ -210,57 +213,59 @@ En Python, et en Vyper, vous pouvez aussi écrire des commentaires en spécifian self.minter = msg.sender ``` -Pour accéder aux variables d'état, on utilise `self.` (encore une fois, comme en Python). +Pour accéder aux variables d'état, vous utilisez `self.`(encore une fois, comme en Python). -#### Fonctions « view » {#views} +#### Fonctions de vue {#views} -Ce sont des fonctions qui ne modifient pas l'état de la blockchain, et qui peuvent donc être exécutées gratuitement lorsqu'elles sont appelées en externe. Si ces fonctions sont appelées par un contrat, elles doivent quand-même être exécutées sur chaque nœud, et coûtent ainsi du gaz. +Ce sont des fonctions qui ne modifient pas l'état de la blockchain et qui peuvent donc être exécutées gratuitement si elles sont appelées en externe. Si les fonctions de vue sont appelées par un contrat, elles doivent tout de même être exécutées sur chaque nœud et coûtent donc du gaz. ```python @view @external ``` -Ces mot-clés commençant par une arobase (`@`) précèdent une définition de fonction et s'appellent des _décorateurs_. Ils déterminent les circonstances dans lesquelles une fonction peut être appelée. +Ces mots-clés précédant une définition de fonction qui commencent par un signe « at » (`@`) sont appelés des _décorations_. Ils spécifient les circonstances dans lesquelles une fonction peut être appelée. -- `@view` spécifie que la fonction est de type « view ». -- `@external` spécifie que cette fonction peut être appelée par des transactions et par d'autres contrats. +- `@view` spécifie que cette fonction est une vue. +- `@external` spécifie que cette fonction particulière peut être appelée par des transactions et par d'autres contrats. ```python def supportsInterface(_interfaceID: bytes32) -> bool: ``` -À la différence de Python, Vyper est un [langage à typage statique](https://wikipedia.org/wiki/Type_system#Static_type_checking). Vous ne pouvez pas déclarer une variable, ou un paramètre de fonction, sans préciser le [type de donnée](https://vyper.readthedocs.io/en/latest/types.html). Dans le cas présent, le paramètre d'entrée est `bytes32`, une valeur de 256 bits (cela correspond à la longueur de mot native au sein de la [machine virtuelle Ethereum](/developers/docs/evm/)). La valeur de sortie est de type booléen. Par convention, les noms des paramètres d'une fonction commencent par un tiret bas (`_`). +Contrairement à Python, Vyper est un [langage à typage statique](https://wikipedia.org/wiki/Type_system#Static_type_checking). +Vous ne pouvez pas déclarer une variable, ou un paramètre de fonction, sans identifier le [type de données](https://vyper.readthedocs.io/en/latest/types.html). Dans ce cas, le paramètre d'entrée est `bytes32`, une valeur de 256 bits (256 bits est la taille de mot native de la [machine virtuelle Ethereum](/developers/docs/evm/)). La sortie est une valeur booléenne. Par convention, les noms des paramètres de fonction commencent par un trait de soulignement (`_`). ```python """ - @dev Interface identification is specified in ERC-165. - @param _interfaceID Id of the interface + @dev L'identification de l'interface est spécifiée dans l'ERC-165. + @param _interfaceID ID de l'interface """ return self.supportedInterfaces[_interfaceID] ``` -Retourne la valeur de type HashMap contenue dans `self.supportedInterfaces`, qui est définie dans le constructeur (`__init__`). +Retourne la valeur du HashMap `self.supportedInterfaces`, qui est définie dans le constructeur (`__init__`). ```python -### VIEW FUNCTIONS ### +### FONCTIONS DE VUE ### + ``` -Ce sont les fonctions « view » qui rendent les informations sur les jetons accessibles aux utilisateurs et aux autres contrats. +Ce sont les fonctions de vue qui rendent les informations sur les jetons disponibles pour les utilisateurs et autres contrats. ```python @view @external def balanceOf(_owner: address) -> uint256: """ - @dev Returns the number of NFTs owned by `_owner`. - Throws if `_owner` is the zero address. NFTs assigned to the zero address are considered invalid. - @param _owner Address for whom to query the balance. + @dev Retourne le nombre de NFT détenus par `_owner`. + Lance une exception si `_owner` est l'adresse zéro. Les NFT attribués à l'adresse zéro sont considérés comme non valides. + @param _owner Adresse pour laquelle interroger le solde. """ assert _owner != ZERO_ADDRESS ``` -Cette ligne [confirme](https://vyper.readthedocs.io/en/latest/statements.html#assert) le fait qu'`_owner` n'est pas égal à zéro. Si c'est le cas, il y a une erreur et l'opération est annulée. +Cette ligne [affirme](https://vyper.readthedocs.io/en/latest/statements.html#assert) que `_owner` n'est pas zéro. Si c'est le cas, il y a une erreur et l'opération est annulée. ```python return self.ownerToNFTokenCount[_owner] @@ -269,70 +274,73 @@ Cette ligne [confirme](https://vyper.readthedocs.io/en/latest/statements.html#as @external def ownerOf(_tokenId: uint256) -> address: """ - @dev Returns the address of the owner of the NFT. - Throws if `_tokenId` is not a valid NFT. - @param _tokenId The identifier for an NFT. + @dev Retourne l'adresse du propriétaire du NFT. + Lance une exception si `_tokenId` n'est pas un NFT valide. + @param _tokenId L'identifiant d'un NFT. """ owner: address = self.idToOwner[_tokenId] - # Throws if `_tokenId` is not a valid NFT + # Lance une exception si `_tokenId` n'est pas un NFT valide assert owner != ZERO_ADDRESS return owner ``` -Dans la machine virtuelle Ethereum (EVM), tout espace mémoire qui n'a pas de valeur stockée contient zéro. Si la valeur de `_tokenId` ne contient pas de jeton, alors la valeur de `self.idToOwner[_tokenId]` est égale à zéro. Dans ce cas, la fonction annule son exécution. +Dans la machine virtuelle Ethereum (EVM), tout stockage qui ne contient pas de valeur stockée est égal à zéro. +S'il n'y a pas de jeton à `_tokenId`, alors la valeur de `self.idToOwner[_tokenId]` est zéro. Dans ce cas, la fonction est annulée. ```python @view @external def getApproved(_tokenId: uint256) -> address: """ - @dev Get the approved address for a single NFT. - Throws if `_tokenId` is not a valid NFT. - @param _tokenId ID of the NFT to query the approval of. + @dev Obtient l'adresse approuvée pour un seul NFT. + Lance une exception si `_tokenId` n'est pas un NFT valide. + @param _tokenId ID du NFT pour lequel interroger l'approbation. """ - # Throws if `_tokenId` is not a valid NFT + # Lance une exception si `_tokenId` n'est pas un NFT valide assert self.idToOwner[_tokenId] != ZERO_ADDRESS return self.idToApprovals[_tokenId] ``` -Notez que `getApproved` _peut_ retourner zéro. Si le jeton est valide, la fonction retourne `self.idToApprovals[_tokenId]`. S'il n'y a pas d'approbateur, la valeur est égale à zéro. +Notez que `getApproved` _peut_ retourner zéro. Si le jeton est valide, il retourne `self.idToApprovals[_tokenId]`. +S'il n'y a pas d'approbateur, cette valeur est zéro. ```python @view @external def isApprovedForAll(_owner: address, _operator: address) -> bool: """ - @dev Checks if `_operator` is an approved operator for `_owner`. - @param _owner The address that owns the NFTs. - @param _operator The address that acts on behalf of the owner. + @dev Vérifie si `_operator` est un opérateur approuvé pour `_owner`. + @param _owner L'adresse qui possède les NFT. + @param _operator L'adresse qui agit au nom du propriétaire. """ return (self.ownerToOperators[_owner])[_operator] ``` -La fonction vérifie qu'`_operator` est autorisé à gérer tous les jetons d'`_owner` dans ce contrat. Comme il peut y avoir plusieurs opérateurs, il s'agit d'une HashMap à deux dimensions. +Cette fonction vérifie si `_operator` est autorisé à gérer tous les jetons de `_owner` dans ce contrat. +Comme il peut y avoir plusieurs opérateurs, il s'agit d'un HashMap à deux niveaux. -#### Fonctions relatives au transfert {#transfer-helpers} +#### Fonctions d'aide au transfert {#transfer-helpers} -Ces fonctions effectuent des opérations qui concernent le transfert ou la gestion des jetons. +Ces fonctions implémentent des opérations qui font partie du transfert ou de la gestion des jetons. ```python -### TRANSFER FUNCTION HELPERS ### +### FONCTIONS D'AIDE AU TRANSFERT ### @view @internal ``` -Ce décorateur, `@internal`, signifie que la fonction est uniquement accessible aux autres fonctions de ce contrat. Par convention, le nom de ces fonctions commence par un tiret bas (`_`). +Cette décoration, `@internal`, signifie que la fonction n'est accessible qu'à partir d'autres fonctions du même contrat. Par convention, ces noms de fonction commencent également par un trait de soulignement (`_`). ```python def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: """ - @dev Returns whether the given spender can transfer a given token ID - @param spender address of the spender to query - @param tokenId uint256 ID of the token to be transferred - @return bool whether the msg.sender is approved for the given token ID, - is an operator of the owner, or is the owner of the token + @dev Retourne si le dépensier donné peut transférer un ID de jeton donné + @param spender adresse du dépensier à interroger + @param tokenId uint256 ID du jeton à transférer + @return bool si le msg.sender est approuvé pour l'ID de jeton donné, + est un opérateur du propriétaire, ou est le propriétaire du jeton """ owner: address = self.idToOwner[_tokenId] spenderIsOwner: bool = owner == _spender @@ -341,117 +349,117 @@ def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll ``` -Il existe trois façons d'autoriser une adresse à transférer un jeton : +Il y a trois façons pour une adresse d'être autorisée à transférer un jeton : 1. L'adresse est le propriétaire du jeton -2. L'adresse est autorisée à utiliser ce jeton +2. L'adresse est approuvée pour dépenser ce jeton 3. L'adresse est un opérateur pour le propriétaire du jeton -La fonction ci-dessus peut être « view », car elle ne modifie par l'état. Pour réduire les coûts d'exécution, toute fonction qui _peut_ être « view » _devrait_ être « view ». +La fonction ci-dessus peut être une vue car elle ne modifie pas l'état. Pour réduire les coûts d'exploitation, toute fonction qui _peut_ être une vue _devrait_ être une vue. ```python @internal def _addTokenTo(_to: address, _tokenId: uint256): """ - @dev Add a NFT to a given address - Throws if `_tokenId` is owned by someone. + @dev Ajoute un NFT à une adresse donnée + Lance une exception si `_tokenId` est détenu par quelqu'un. """ - # Throws if `_tokenId` is owned by someone + # Lance une exception si `_tokenId` est détenu par quelqu'un assert self.idToOwner[_tokenId] == ZERO_ADDRESS - # Change the owner + # Change le propriétaire self.idToOwner[_tokenId] = _to - # Change count tracking + # Change le suivi du compte self.ownerToNFTokenCount[_to] += 1 @internal def _removeTokenFrom(_from: address, _tokenId: uint256): """ - @dev Remove a NFT from a given address - Throws if `_from` is not the current owner. + @dev Supprime un NFT d'une adresse donnée + Lance une exception si `_from` n'est pas le propriétaire actuel. """ - # Throws if `_from` is not the current owner + # Lance une exception si `_from` n'est pas le propriétaire actuel assert self.idToOwner[_tokenId] == _from - # Change the owner + # Change le propriétaire self.idToOwner[_tokenId] = ZERO_ADDRESS - # Change count tracking + # Change le suivi du compte self.ownerToNFTokenCount[_from] -= 1 ``` -Lorsqu'il y a un problème avec un transfert, on annule l'appel de la fonction. +En cas de problème avec un transfert, nous annulons l'appel. ```python @internal def _clearApproval(_owner: address, _tokenId: uint256): """ - @dev Clear an approval of a given address - Throws if `_owner` is not the current owner. + @dev Efface une approbation d'une adresse donnée + Lance une exception si `_owner` n'est pas le propriétaire actuel. """ - # Throws if `_owner` is not the current owner + # Lance une exception si `_owner` n'est pas le propriétaire actuel assert self.idToOwner[_tokenId] == _owner if self.idToApprovals[_tokenId] != ZERO_ADDRESS: - # Reset approvals + # Réinitialise les approbations self.idToApprovals[_tokenId] = ZERO_ADDRESS ``` -Ne changez cette valeur que si c'est nécessaire. Les variables d'état se trouvent dans le stockage. Modifier le stockage est une des opérations les plus coûteuses que l'EVM (la machine virtuelle Ethereum) peut effectuer (en termes de [gaz](/developers/docs/gas/)). Par conséquent, il est préférable de l'éviter, même pour écrire que la valeur existante a un coût élevé. +Ne modifiez la valeur que si nécessaire. Les variables d'état vivent dans le stockage. L'écriture dans le stockage est l'une des opérations les plus coûteuses que l'EVM (machine virtuelle Ethereum) effectue (en termes de [gaz](/developers/docs/gas/)). Par conséquent, il est conseillé de la minimiser, même l'écriture de la valeur existante a un coût élevé. ```python @internal def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address): """ - @dev Execute transfer of a NFT. - Throws unless `msg.sender` is the current owner, an authorized operator, or the approved - address for this NFT. (NOTE: `msg.sender` not allowed in private function so pass `_sender`.) - Throws if `_to` is the zero address. - Throws if `_from` is not the current owner. - Throws if `_tokenId` is not a valid NFT. + @dev Exécute le transfert d'un NFT. + Lance une exception sauf si `msg.sender` est le propriétaire actuel, un opérateur autorisé, ou l'adresse + approuvée pour ce NFT. (NOTE : `msg.sender` n'est pas autorisé dans une fonction privée, donc passez `_sender`.) + Lance une exception si `_to` est l'adresse zéro. + Lance une exception si `_from` n'est pas le propriétaire actuel. + Lance une exception si `_tokenId` n'est pas un NFT valide. """ ``` -Nous avons cette fonction interne à disposition parce qu'il existe deux façons de transférer des jetons (« normale » et « sûre »), mais nous souhaitons que cela ne se passe qu'à un seul endroit du code, afin de simplifier l'audit. +Nous avons cette fonction interne car il y a deux façons de transférer des jetons (régulière et sûre), mais nous ne voulons qu'un seul emplacement dans le code où nous le faisons pour faciliter l'audit. ```python - # Check requirements + # Vérifier les exigences assert self._isApprovedOrOwner(_sender, _tokenId) - # Throws if `_to` is the zero address + # Lance une exception si `_to` est l'adresse zéro assert _to != ZERO_ADDRESS - # Clear approval. Throws if `_from` is not the current owner + # Effacer l'approbation. Lance une exception si `_from` n'est pas le propriétaire actuel self._clearApproval(_from, _tokenId) - # Remove NFT. Throws if `_tokenId` is not a valid NFT + # Supprimer le NFT. Lance une exception si `_tokenId` n'est pas un NFT valide self._removeTokenFrom(_from, _tokenId) - # Add NFT + # Ajouter le NFT self._addTokenTo(_to, _tokenId) - # Log the transfer + # Journaliser le transfert log Transfer(_from, _to, _tokenId) ``` -Pour émettre un évènement dans Vyper, on utilise le mot-clé `log` ([cliquer ici pour plus de détails](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging)). +Pour émettre un événement en Vyper, vous utilisez une instruction `log` ([voir ici pour plus de détails](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging)). #### Fonctions de transfert {#transfer-funs} ```python -### TRANSFER FUNCTIONS ### +### FONCTIONS DE TRANSFERT ### @external def transferFrom(_from: address, _to: address, _tokenId: uint256): """ - @dev Throws unless `msg.sender` is the current owner, an authorized operator, or the approved - address for this NFT. - Throws if `_from` is not the current owner. - Throws if `_to` is the zero address. - Throws if `_tokenId` is not a valid NFT. - @notice The caller is responsible to confirm that `_to` is capable of receiving NFTs or else - they maybe be permanently lost. - @param _from The current owner of the NFT. - @param _to The new owner. - @param _tokenId The NFT to transfer. + @dev Lance une exception sauf si `msg.sender` est le propriétaire actuel, un opérateur autorisé ou l'adresse + approuvée pour ce NFT. + Lance une exception si `_from` n'est pas le propriétaire actuel. + Lance une exception si `_to` est l'adresse zéro. + Lance une exception si `_tokenId` n'est pas un NFT valide. + @notice L'appelant est responsable de confirmer que `_to` est capable de recevoir des NFT, sinon + ils pourraient être perdus de manière permanente. + @param _from Le propriétaire actuel du NFT. + @param _to Le nouveau propriétaire. + @param _tokenId Le NFT à transférer. """ self._transferFrom(_from, _to, _tokenId, msg.sender) ``` -Cette fonction permet d'effectuer un transfert vers l'adresse souhaitée. À moins que l'adresse représente un utilisateur ou un contract capable de transférer des jetons, tout jeton que vous transférez sera bloqué à cette adresse et rendu inutilisable. +Cette fonction vous permet de transférer vers une adresse arbitraire. À moins que l'adresse ne soit un utilisateur ou un contrat qui sait comment transférer des jetons, tout jeton que vous transférez sera bloqué dans cette adresse et inutilisable. ```python @external @@ -462,30 +470,30 @@ def safeTransferFrom( _data: Bytes[1024]=b"" ): """ - @dev Transfers the ownership of an NFT from one address to another address. - Throws unless `msg.sender` is the current owner, an authorized operator, or the - approved address for this NFT. - Throws if `_from` is not the current owner. - Throws if `_to` is the zero address. - Throws if `_tokenId` is not a valid NFT. - If `_to` is a smart contract, it calls `onERC721Received` on `_to` and throws if - the return value is not `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. - NOTE: bytes4 is represented by bytes32 with padding - @param _from The current owner of the NFT. - @param _to The new owner. - @param _tokenId The NFT to transfer. - @param _data Additional data with no specified format, sent in call to `_to`. + @dev Transfère la propriété d'un NFT d'une adresse à une autre. + Lance une exception sauf si `msg.sender` est le propriétaire actuel, un opérateur autorisé ou l'adresse + approuvée pour ce NFT. + Lance une exception si `_from` n'est pas le propriétaire actuel. + Lance une exception si `_to` est l'adresse zéro. + Lance une exception si `_tokenId` n'est pas un NFT valide. + Si `_to` est un contrat intelligent, il appelle `onERC721Received` sur `_to` et lance une exception si + la valeur de retour n'est pas `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + NOTE : bytes4 est représenté par bytes32 avec remplissage + @param _from Le propriétaire actuel du NFT. + @param _to Le nouveau propriétaire. + @param _tokenId Le NFT à transférer. + @param _data Données supplémentaires sans format spécifié, envoyées dans l'appel à `_to`. """ self._transferFrom(_from, _to, _tokenId, msg.sender) ``` -Il est correct d'effectuer le transfert au début parce qu'en cas de problème, nous allons de toute façon revenir en arrière, ce qui revient à annuler tout ce qui aura été fait durant l'appel. +Il est acceptable de faire le transfert en premier car en cas de problème, nous allons de toute façon annuler, donc tout ce qui a été fait dans l'appel sera annulé. ```python - if _to.is_contract: # check if `_to` is a contract address + if _to.is_contract: # vérifier si `_to` est une adresse de contrat ``` -Vérifiez d'abord si l'adresse est un contrat (si elle comporte du code). Si ce n'est pas le cas, il s'agit d'une adresse utilisateur et celui-ci pourra alors utiliser le jeton ou bien le transférer. Mais ne vous laissez pas tromper par un faux sentiment de sécurité. Vous pouvez perdre vos jetons, même avec `safeTransferFrom`, si vous les transférez vers une adresse dont personne ne connaît la clé privée. +Vérifiez d'abord si l'adresse est un contrat (si elle a du code). Sinon, supposez qu'il s'agit d'une adresse d'utilisateur et que l'utilisateur pourra utiliser le jeton ou le transférer. Mais ne vous laissez pas bercer par un faux sentiment de sécurité. Vous pouvez perdre des jetons, même avec `safeTransferFrom`, si vous les transférez à une adresse dont personne ne connaît la clé privée. ```python returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) @@ -494,34 +502,34 @@ Vérifiez d'abord si l'adresse est un contrat (si elle comporte du code). Si ce Appelez le contrat cible pour voir s'il peut recevoir des jetons ERC-721. ```python - # Throws if transfer destination is a contract which does not implement 'onERC721Received' + # Lance une exception si la destination du transfert est un contrat qui n'implémente pas 'onERC721Received' assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32) ``` -Si le destinataire est un contrat, mais qui ne peut pas recevoir de jetons ERC-721 (ou qui a choisi de refuser ce transfert en particulier), annulez. +Si la destination est un contrat, mais qui n'accepte pas les jetons ERC-721 (ou qui a décidé de ne pas accepter ce transfert particulier), annulez. ```python @external def approve(_approved: address, _tokenId: uint256): """ - @dev Set or reaffirm the approved address for an NFT. The zero address indicates there is no approved address. - Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner. - Throws if `_tokenId` is not a valid NFT. (NOTE: This is not written the EIP) - Throws if `_approved` is the current owner. (NOTE: This is not written the EIP) - @param _approved Address to be approved for the given NFT ID. - @param _tokenId ID of the token to be approved. + @dev Définit ou reconfirme l'adresse approuvée pour un NFT. L'adresse zéro indique qu'il n'y a pas d'adresse approuvée. + Lance une exception sauf si `msg.sender` est le propriétaire actuel du NFT, ou un opérateur autorisé du propriétaire actuel. + Lance une exception si `_tokenId` n'est pas un NFT valide. (NOTE : Ceci n'est pas écrit dans l'EIP) + Lance une exception si `_approved` est le propriétaire actuel. (NOTE : Ceci n'est pas écrit dans l'EIP) + @param _approved Adresse à approuver pour l'ID de NFT donné. + @param _tokenId ID du jeton à approuver. """ owner: address = self.idToOwner[_tokenId] - # Throws if `_tokenId` is not a valid NFT + # Lance une exception si `_tokenId` n'est pas un NFT valide assert owner != ZERO_ADDRESS - # Throws if `_approved` is the current owner + # Lance une exception si `_approved` est le propriétaire actuel assert _approved != owner ``` -Par convention, si vous ne souhaitez pas avoir d'approbateur, vous désignez l'adresse zéro, pas la vôtre. +Par convention, si vous ne voulez pas avoir d'approbateur, vous désignez l'adresse zéro, et non vous-même. ```python - # Check requirements + # Vérifier les exigences senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender] assert (senderIsOwner or senderIsApprovedForAll) @@ -530,7 +538,7 @@ Par convention, si vous ne souhaitez pas avoir d'approbateur, vous désignez l'a Pour définir une approbation, vous pouvez être soit le propriétaire, soit un opérateur autorisé par le propriétaire. ```python - # Set the approval + # Définir l'approbation self.idToApprovals[_tokenId] = _approved log Approval(owner, _approved, _tokenId) @@ -538,14 +546,14 @@ Pour définir une approbation, vous pouvez être soit le propriétaire, soit un @external def setApprovalForAll(_operator: address, _approved: bool): """ - @dev Enables or disables approval for a third party ("operator") to manage all of - `msg.sender`'s assets. It also emits the ApprovalForAll event. - Throws if `_operator` is the `msg.sender`. (NOTE: This is not written the EIP) - @notice This works even if sender doesn't own any tokens at the time. - @param _operator Address to add to the set of authorized operators. - @param _approved True if the operators is approved, false to revoke approval. + @dev Active ou désactive l'approbation pour un tiers ("opérateur") afin de gérer tous les + actifs de `msg.sender`. Il émet également l'événement ApprovalForAll. + Lance une exception si `_operator` est le `msg.sender`. (NOTE : Ceci n'est pas écrit dans l'EIP) + @notice Cela fonctionne même si l'expéditeur ne possède aucun jeton au moment de l'exécution. + @param _operator Adresse à ajouter à l'ensemble des opérateurs autorisés. + @param _approved True si les opérateurs sont approuvés, false pour révoquer l'approbation. """ - # Throws if `_operator` is the `msg.sender` + # Lance une exception si `_operator` est le `msg.sender` assert _operator != msg.sender self.ownerToOperators[msg.sender][_operator] = _approved log ApprovalForAll(msg.sender, _operator, _approved) @@ -553,10 +561,10 @@ def setApprovalForAll(_operator: address, _approved: bool): #### Frapper de nouveaux jetons et détruire ceux existants {#mint-burn} -Le compte qui a créé le contrat est le `minter`, un super-utilisateur autorisé à frapper de nouveaux NFT. Cependant, même lui n'est pas autorisé à détruire des jetons existants. Seul le propriétaire, ou une entité autorisée par le propriétaire, peut faire cela. +Le compte qui a créé le contrat est le `minter`, le super utilisateur autorisé à frapper de nouveaux NFT. Cependant, même lui n'est pas autorisé à détruire des jetons existants. Seul le propriétaire, ou une entité autorisée par le propriétaire, peut le faire. ```python -### MINT & BURN FUNCTIONS ### +### FONCTIONS DE FRAPPE ET DE DESTRUCTION ### @external def mint(_to: address, _tokenId: uint256) -> bool: @@ -566,67 +574,70 @@ Cette fonction retourne toujours `True`, car si l'opération échoue, elle est a ```python """ - @dev Function to mint tokens - Throws if `msg.sender` is not the minter. - Throws if `_to` is zero address. - Throws if `_tokenId` is owned by someone. - @param _to The address that will receive the minted tokens. - @param _tokenId The token id to mint. - @return A boolean that indicates if the operation was successful. + @dev Fonction pour frapper des jetons + Lance une exception si `msg.sender` n'est pas le minter. + Lance une exception si `_to` est l'adresse zéro. + Lance une exception si `_tokenId` est détenu par quelqu'un. + @param _to L'adresse qui recevra les jetons frappés. + @param _tokenId L'ID du jeton à frapper. + @return Un booléen qui indique si l'opération a réussi. """ - # Throws if `msg.sender` is not the minter + # Lance une exception si `msg.sender` n'est pas le minter assert msg.sender == self.minter ``` -Seul le minter (le compte qui a créé le contrat ERC-721) peut créer de nouveaux jetons. Cela peut être un problème à l'avenir dans le cas où l'on souhaiterait changer l'identité du minter. Dans un contrat en production, vous voudriez probablement une fonction qui permette au minter de transférer des privilèges de minter à quelqu'un d'autre. +Seul le minter (le compte qui a créé le contrat ERC-721) peut frapper de nouveaux jetons. Cela peut poser un problème à l'avenir si nous voulons changer l'identité du minter. Dans un contrat en production, vous voudriez probablement une fonction qui permet au minter de transférer les privilèges de minter à quelqu'un d'autre. ```python - # Throws if `_to` is zero address + # Lance une exception si `_to` est l'adresse zéro assert _to != ZERO_ADDRESS - # Add NFT. Throws if `_tokenId` is owned by someone + # Ajoute un NFT. Lance une exception si `_tokenId` est détenu par quelqu'un self._addTokenTo(_to, _tokenId) log Transfer(ZERO_ADDRESS, _to, _tokenId) return True ``` -Par convention, la création de nouveaux jetons compte comme un transfert depuis l'adresse zéro. +Par convention, la frappe de nouveaux jetons est considérée comme un transfert depuis l'adresse zéro. ```python @external def burn(_tokenId: uint256): """ - @dev Burns a specific ERC721 token. - Throws unless `msg.sender` is the current owner, an authorized operator, or the approved - address for this NFT. - Throws if `_tokenId` is not a valid NFT. - @param _tokenId uint256 id of the ERC721 token to be burned. + @dev Brûle un jeton ERC721 spécifique. + Lance une exception sauf si `msg.sender` est le propriétaire actuel, un opérateur autorisé, ou l'adresse + approuvée pour ce NFT. + Lance une exception si `_tokenId` n'est pas un NFT valide. + @param _tokenId uint256 ID du jeton ERC721 à brûler. """ - # Check requirements + # Vérifier les exigences assert self._isApprovedOrOwner(msg.sender, _tokenId) owner: address = self.idToOwner[_tokenId] - # Throws if `_tokenId` is not a valid NFT + # Lance une exception si `_tokenId` n'est pas un NFT valide assert owner != ZERO_ADDRESS self._clearApproval(owner, _tokenId) self._removeTokenFrom(owner, _tokenId) log Transfer(owner, ZERO_ADDRESS, _tokenId) ``` -Toute personne autorisée à transférer un jeton est autorisée à le détruire. Bien que détruire un jeton semble équivalent à le transférer à l'adresse zéro, celle-ci ne reçoit pas réellement le jeton. Cela nous permet de libérer tout l'espace de stockage qui était utilisé pour le jeton, ce qui peut réduire les frais de gaz de la transaction. +Toute personne autorisée à transférer un jeton est autorisée à le détruire. Bien que la destruction d'un jeton semble équivalente à un transfert vers l'adresse zéro, l'adresse zéro ne reçoit pas réellement le jeton. Cela nous permet de libérer tout le stockage qui a été utilisé pour le jeton, ce qui peut réduire le coût en gaz de la transaction. -## Utiliser ce contrat {#using-contract} +## Utilisation de ce contrat {#using-contract} -Contrairement à Solidity, Vyper n'a pas de système d'héritage. Il s'agit d'un choix de conception délibéré visant à rendre le code plus clair et donc plus facile à sécuriser. Donc pour créer votre propre contrat Vyper ERC-721, vous prenez [ce contrat](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy) puis vous le modifiez en fonction de la stratégie commerciale que vous souhaitez mettre en œuvre. +Contrairement à Solidity, Vyper n'a pas d'héritage. C'est un choix de conception délibéré pour rendre le code plus clair et donc plus facile à sécuriser. Donc, pour créer votre propre contrat Vyper ERC-721, vous prenez [ce contrat](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy) et le modifiez pour implémenter la logique métier que vous souhaitez. -### Conclusion {#conclusion} +## Conclusion {#conclusion} -Voici les principaux points à retenir sur ce contrat : +Pour récapituler, voici quelques-unes des idées les plus importantes de ce contrat : -- Pour recevoir des jetons ERC-721 par un transfert sécurisé, les contrats doivent implémenter l'interface `ERC721Receiver`. -- Même si vous effectuez un transfert sécurisé, les jetons peuvent rester bloqués si vous les envoyez à une adresse dont la clé privée est inconnue. -- Lorsqu'il y a un problème avec une opération, il est judicieux de `revert` (annuler) l'appel, plutôt que de simplement retourner une valeur d'échec. -- Les tokens ERC-721 existent lorsqu'ils ont un propriétaire. +- Pour recevoir des jetons ERC-721 avec un transfert sécurisé, les contrats doivent implémenter l'interface `ERC721Receiver`. +- Même si vous utilisez un transfert sécurisé, les jetons peuvent toujours être bloqués si vous les envoyez à une adresse dont la clé privée est inconnue. +- En cas de problème avec une opération, il est conseillé d'annuler (`revert`) l'appel, plutôt que de simplement retourner une valeur d'échec. +- Les jetons ERC-721 existent lorsqu'ils ont un propriétaire. - Il existe trois façons d'être autorisé à transférer un NFT. Vous pouvez être le propriétaire, être approuvé pour un jeton spécifique, ou être un opérateur pour tous les jetons du propriétaire. -- Les évènements antérieurs sont uniquement visibles en dehors de la blockchain. Le code exécuté à l'intérieur de la blockchain ne peut pas les voir. +- Les événements passés ne sont visibles qu'en dehors de la blockchain. Le code exécuté à l'intérieur de la blockchain ne peut pas les voir. + +Maintenant, allez implémenter des contrats Vyper sécurisés. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). -Vous êtes maintenant prêts à implémenter des contrats Vyper sécurisés. diff --git a/public/content/translations/fr/developers/tutorials/erc20-annotated-code/index.md b/public/content/translations/fr/developers/tutorials/erc20-annotated-code/index.md index 2efbab42acd..9dc827476f4 100644 --- a/public/content/translations/fr/developers/tutorials/erc20-annotated-code/index.md +++ b/public/content/translations/fr/developers/tutorials/erc20-annotated-code/index.md @@ -3,28 +3,33 @@ title: "Présentation du contrat ERC-20" description: Qu'est-ce que le contrat OpenZeppelin ERC-20 et pourquoi existe-t-il ? author: Ori Pomerantz lang: fr -tags: - - "solidity" - - "erc-20" +tags: [ "solidité", "erc-20" ] skill: beginner published: 2021-03-09 --- ## Introduction {#introduction} -Ethereum est couramment utilisé par des groupes pour créer des jetons échangeables ou, dans un certain sens, leur propre monnaie. Ces jetons suivent généralement une norme, [ERC-20](/developers/docs/standards/tokens/erc-20/). Cette norme permet d'écrire des outils, tels que des groupes de liquidité et des portefeuilles, qui fonctionnent avec tous les jetons ERC-20. . Dans cet article nous allons analyser [l'implémentation d'OpenZeppelin Solidity ERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol), ainsi que la [définition d'interface](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol). +Ethereum est couramment utilisé par des groupes pour créer des jetons échangeables ou, dans un certain sens, leur propre monnaie. Ces jetons suivent généralement une norme, +[ERC-20](/developers/docs/standards/tokens/erc-20/). Cette norme permet de créer des outils, tels que des pools de liquidités et des portefeuilles, qui fonctionnent avec tous les jetons ERC-20 +. Dans cet article, nous analyserons l'[implémentation ERC20 de Solidity par OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol), ainsi que la [définition de l'interface](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol). -Ceci est le code source annoté. Si vous voulez implémenter ERC-20, [lisez ce tutoriel](https://docs.openzeppelin.com/contracts/2.x/erc20-supply). +Ceci est le code source annoté. Si vous souhaitez implémenter l'ERC-20, +[lisez ce tutoriel](https://docs.openzeppelin.com/contracts/2.x/erc20-supply). ## L'interface {#the-interface} -L'objectif d'un standard comme ERC-20 est de permettre de nombreuses implémentations de jetons interopérables entre applications, comme les portefeuilles et les échanges décentralisés. À cette fin, nous créons une [interface](https://www.geeksforgeeks.org/solidity-basics-of-interface/). Tout code qui a besoin d'utiliser le contrat de jeton peut employer les mêmes définitions dans l'interface et ainsi être compatible avec tous les contrats de jetons qui l'utilisent, qu'il s'agisse d'un portefeuille tel que MetaMask, une DApp comme etherscan.io, ou un contrat différent tel qu'un pool de liquidités. +L'objectif d'une norme comme l'ERC-20 est de permettre de nombreuses implémentations de jetons interopérables entre les applications, comme les portefeuilles et les échanges décentralisés. Pour ce faire, nous créons une +[interface](https://www.geeksforgeeks.org/solidity/solidity-basics-of-interface/). Tout code qui a besoin d'utiliser le contrat de jeton peut employer les mêmes définitions dans l'interface et être compatible avec tous les contrats de jetons qui l'utilisent, qu'il s'agisse d'un portefeuille tel que MetaMask, d'une dapp comme etherscan.io, ou d'un contrat différent tel qu'un pool de liquidités. ![Illustration de l'interface ERC-20](erc20_interface.png) -Si vous êtes un programmeur expérimenté, vous vous souvenez probablement avoir déjà vu des constructions similaires dans [Java](https://www.w3schools.com/java/java_interface.asp) ou même dans des [fichiers d'en-tête C](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html). +Si vous êtes un programmeur expérimenté, vous vous souvenez probablement avoir vu des constructions similaires en [Java](https://www.w3schools.com/java/java_interface.asp) +ou même dans des [fichiers d'en-tête C](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html). -Ceci est une définition de [l'interface ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) d'OpenZeppelin. C'est une traduction du [standard lisible par l'homme](https://eips.ethereum.org/EIPS/eip-20) en code Solidity. Bien sûr, l'interface elle-même ne définit pas _comment_ faire quoi que ce soit. Ceci est expliqué dans le code source du contrat ci-dessous. +Ceci est une définition de l'[Interface ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) +d'OpenZeppelin. C'est une traduction de la [norme lisible par l'homme](https://eips.ethereum.org/EIPS/eip-20) en code Solidity. Bien sûr, l'interface +elle-même ne définit pas _comment_ faire quoi que ce soit. Ceci est expliqué dans le code source du contrat ci-dessous.   @@ -32,7 +37,8 @@ Ceci est une définition de [l'interface ERC-20](https://github.com/OpenZeppelin // SPDX-License-Identifier: MIT ``` -Les fichiers Solidity sont supposés inclure un identifiant de licence. [Vous pouvez consulter la liste des licences ici](https://spdx.org/licenses/). Si vous avez besoin d'une licence différente, dites de quoi il s'agit dans les commentaires. +Les fichiers Solidity sont censés inclure un identifiant de licence. [Vous pouvez voir la liste des licences ici](https://spdx.org/licenses/). Si vous avez besoin d'une autre +licence, il vous suffit de l'expliquer dans les commentaires.   @@ -40,17 +46,20 @@ Les fichiers Solidity sont supposés inclure un identifiant de licence. [Vous po pragma solidity >=0.6.0 <0.8.0; ``` -Le langage Solidity évolue rapidement et les nouvelles versions peuvent ne pas être compatibles avec l'ancien code ([voir ici](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html)). Par conséquent, c'est une bonne idée de ne pas spécifier seulement une version minimale du langage, mais également une version maximale, la dernière avec laquelle vous avez testé le code. +Le langage Solidity évolue encore rapidement, et les nouvelles versions peuvent ne pas être compatibles avec l'ancien code +([voir ici](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html)). Par conséquent, il est judicieux de spécifier non seulement une version minimale +du langage, mais aussi une version maximale, la plus récente avec laquelle vous avez testé le code.   ```solidity /** - * @dev Interface of the ERC20 standard as defined in the EIP. + * @dev Interface de la norme ERC20 telle que définie dans l'EIP. */ ``` -Le `@dev` dans le commentaire fait partie du [format NatSpec](https://docs.soliditylang.org/en/develop/natspec-format.html), utilisé pour produire la documentation à partir du code source. +Le `@dev` dans le commentaire fait partie du [format NatSpec](https://docs.soliditylang.org/en/develop/natspec-format.html), utilisé pour produire de la +documentation à partir du code source.   @@ -64,78 +73,100 @@ Par convention, les noms d'interface commencent par `I`. ```solidity /** - * @dev Returns the amount of tokens in existence. + * @dev Renvoie la quantité de jetons existants. */ function totalSupply() external view returns (uint256); ``` -Cette fonction est `external`, ce qui signifie [qu'elle ne peut être appelée qu'extra-contractuellement](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2). Elle retourne la quantité totale de jetons dans le contrat. Cette valeur est fournie en utilisant le type de données le plus commun dans Ethereum, 256 bits non signé (256 bits est la taille native de l'EVM). Cette fonction est également une `view`, ce qui signifie qu'elle ne change pas l'état et peut donc être exécutée sur un seul nœud au lieu de devoir exécuter chaque nœud dans la blockchain. Ce type de fonction ne génère pas de transaction et n'est pas [énergétivore](/developers/docs/gas/). +Cette fonction est `external`, ce qui signifie qu'[elle ne peut être appelée que depuis l'extérieur du contrat](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2). +Elle renvoie la quantité totale de jetons dans le contrat. Cette valeur est renvoyée en utilisant le type de données le plus courant sur Ethereum, un entier non signé de 256 bits (256 bits est la taille +de mot native de l'EVM). Cette fonction est également de type `view`, ce qui signifie qu'elle ne modifie pas l'état. Elle peut donc être exécutée sur un seul nœud, sans que tous les nœuds de la blockchain n'aient à l'exécuter +. Ce type de fonction ne génère pas de transaction et n'a pas de coût en [gaz](/developers/docs/gas/). -**Remarque :** En théorie, on peut avoir l'impression que le créateur d'un contrat est en capacité de tricher en indiquant une quantité totale inférieure à la valeur réelle et en faisant ainsi apparaître chaque jeton plus précieux qu'il ne l'est réellement. Avoir cette crainte c'est, cependant, méconnaître la vraie nature de la blockchain. Tout ce qui se passe sur la blockchain peut être vérifié par chaque nœud. Pour ce faire, le langage de code et le stockage de chaque contrat sont disponibles sur chaque nœud. Vous n'avez pas à publier le code Solidity de votre contrat, mais personne ne vous prendrait au sérieux à moins de publier le code source et la version de Solidity avec laquelle il a été compilé pour qu'il puisse ainsi être comparé au code langue de la machine que vous avez utilisé. Par exemple, voir [ce contrat](https://etherscan.io/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD#code). +**Remarque :** En théorie, il pourrait sembler que le créateur d'un contrat puisse tricher en renvoyant une offre totale inférieure à la valeur réelle, faisant ainsi paraître chaque jeton +plus précieux qu'il ne l'est en réalité. Cependant, cette crainte ignore la véritable nature de la blockchain. Tout ce qui se passe sur la blockchain peut être vérifié par +chaque nœud. Pour ce faire, le code en langage machine et le stockage de chaque contrat sont disponibles sur chaque nœud. Bien que vous ne soyez pas obligé de publier le code Solidity +de votre contrat, personne ne vous prendrait au sérieux si vous ne publiez pas le code source et la version de Solidity avec laquelle il a été compilé, afin qu'il puisse +être vérifié par rapport au code en langage machine que vous avez fourni. +Par exemple, consultez [ce contrat](https://eth.blockscout.com/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD?tab=contract).   ```solidity /** - * @dev Returns the amount of tokens owned by `account`. + * @dev Renvoie la quantité de jetons détenus par `account`. */ function balanceOf(address account) external view returns (uint256); ``` -Comme son nom même l'indique, `balanceOf` permet d'afficher le solde d'un compte. Les comptes Ethereum sont identifiés dans Solidity en utilisant le type d'`address` ayant une valeur de 160 bits. Également `external` et `view`. +Comme son nom l'indique, `balanceOf` renvoie le solde d'un compte. Les comptes Ethereum sont identifiés dans Solidity à l'aide du type `address`, qui contient 160 bits. +Elle est également `external` et `view`.   ```solidity /** - * @dev Moves `amount` tokens from the caller's account to `recipient`. + * @dev Déplace un `montant` (`amount`) de jetons du compte de l'appelant vers le `destinataire` (`recipient`). * - * Returns a boolean value indicating whether the operation succeeded. + * Renvoie une valeur booléenne indiquant si l'opération a réussi. * - * Emits a {Transfer} event. + * Émet un événement {Transfer}. */ function transfer(address recipient, uint256 amount) external returns (bool); ``` -La fonction `transfer` permet de transférer un jeton de l'appelant à une adresse différente. Cela implique un changement d'état, donc ce n'est pas une `view`. Lorsqu'un utilisateur appelle cette fonction, il crée une transaction qui a un coût exprimé en gaz. Il émet également un événement, `Transfer`, pour informer tout le monde sur la blockchain de cet événement. +La fonction `transfer` transfère des jetons de l'appelant à une autre adresse. Cela implique un changement d'état, ce n'est donc pas une fonction de type `view`. +Lorsqu'un utilisateur appelle cette fonction, cela crée une transaction et coûte du gaz. Elle émet également un événement, `Transfer`, pour informer tout le monde sur la +blockchain de l'événement. -La fonction a deux types de sortie pour deux différents types d'appels : +La fonction a deux types de sortie pour deux types d'appelants différents : -- Les utilisateurs qui appellent la fonction directement depuis une interface utilisateur. Typiquement, l'utilisateur soumet une transaction et n'attend pas la réponse, ce qui peut prendre un temps indéfini. L'utilisateur peut voir ce qui s'est passé en recherchant le reçu de transaction (qui est identifié par le hash de transaction) ou en recherchant l'événement `Transfer`. -- Autres contrats qui appellent la fonction dans le cadre d'une transaction globale. Ces contrats donnent immédiatement des résultats parce qu'ils s'exécutent lors de la même transaction, de sorte qu'ils peuvent utiliser la valeur fournie par la fonction. +- Les utilisateurs qui appellent la fonction directement depuis une interface utilisateur. Généralement, l'utilisateur soumet une transaction + et n'attend pas de réponse, ce qui peut prendre un temps indéfini. L'utilisateur peut voir ce qui s'est passé + en consultant le reçu de transaction (identifié par le hachage de la transaction) ou en recherchant l'événement + `Transfer`. +- D'autres contrats, qui appellent la fonction dans le cadre d'une transaction globale. Ces contrats obtiennent le résultat immédiatement, + car ils s'exécutent dans la même transaction, et peuvent donc utiliser la valeur de retour de la fonction. -Les autres fonctions qui changent l'état du contrat donnent le même type de résultat. +Le même type de sortie est créé par les autres fonctions qui modifient l'état du contrat.   -Les allocations permettent à un compte de dépenser des jetons qui appartiennent à un autre propriétaire. C'est utile par exemple, pour les contrats qui agissent en tant que vendeurs. Les contrats ne peuvent pas suivre les événements. Si un acheteur devait par conséquent transférer directement des jetons au contrat du vendeur, ce contrat ne saurait pas qu'il y a eu paiement. Au lieu de cela, l'acheteur permet au contrat du vendeur de dépenser un certain montant, le vendeur transférant ce montant. Cela est possible grâce à une fonction appelée par le contrat du vendeur qui peut savoir si l'opération a réussi. +Les allocations permettent à un compte de dépenser des jetons qui appartiennent à un autre propriétaire. +C'est utile, par exemple, pour les contrats qui agissent en tant que vendeurs. Les contrats ne peuvent +pas surveiller les événements, donc si un acheteur devait transférer directement des jetons au contrat du vendeur +, ce contrat ne saurait pas qu'il a été payé. Au lieu de cela, l'acheteur autorise le contrat du vendeur +à dépenser un certain montant, et le vendeur transfère ce montant. +Cela se fait par le biais d'une fonction que le contrat du vendeur appelle, de sorte que le contrat du vendeur +puisse savoir si l'opération a réussi. ```solidity /** - * @dev Indique le nombre restant de jetons que `spender` sera - * autorisé à dépenser pour le compte du `owner` par l'intermédiaire de {transferFrom}. Ce nombre est - * zéro par défaut. + * @dev Renvoie le nombre de jetons restants que `spender` sera + * autorisé à dépenser au nom de `owner` via {transferFrom}. La valeur par + * défaut est zéro. * - * Cette valeur change lorsque {approve} ou {transferFrom} sont appelés. + * Cette valeur change lorsque {approve} ou {transferFrom} sont appelées. */ function allowance(address owner, address spender) external view returns (uint256); ``` -La fonction `allowance` permet à quiconque de demander à voir quelle allocation une adresse (`owner`) permet à une autre adresse (`spender`) de dépenser. +La fonction `allowance` permet à quiconque de demander à voir quelle est l'allocation qu'une +adresse (`owner`) autorise une autre adresse (`spender`) à dépenser.   ```solidity /** - * @dev Définit l''amount` comme étant l'allocation que le `spender` attribue aux jetons de l'appelant. + * @dev Définit le `montant` (`amount`) comme l'allocation de `spender` sur les jetons de l'appelant. * - * Renvoie une valeur booléenne indiquant si l'opération a réussi. + * Renvoie une valeur booléenne indiquant si l'opération a réussi. * - * IMPORTANT: Attention ! Modifier une allocation par ce biais comporte un risque : - * celui que quelqu'un puisse utiliser à la fois l'ancienne et la nouvelle allocation en activant une - * commande de transactions erronée. Solution possible pour résoudre le problème - * réduire l'allocation du client à 0 et fixer la - * valeur souhaitée par la suite : + * IMPORTANT : Sachez que la modification d'une allocation avec cette méthode comporte le risque + * que quelqu'un puisse utiliser à la fois l'ancienne et la nouvelle allocation en raison d'un + * ordre de transaction malencontreux. Une solution possible pour atténuer cette + * condition de concurrence consiste à d'abord réduire à 0 l'allocation du dépensier, puis à définir la + * valeur souhaitée : * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Émet un événement {Approval}. @@ -143,15 +174,19 @@ La fonction `allowance` permet à quiconque de demander à voir quelle allocatio function approve(address spender, uint256 amount) external returns (bool); ``` -La fonction `approve` crée une autorisation. Veillez à lire le message expliquant comment elle peut être mal utilisée. Sur Ethereum, vous contrôlez l'ordre de vos propres transactions mais vous ne pouvez pas contrôler l'ordre dans lequel les transactions des autres seront exécutées, à moins que vous attendiez pour soumettre votre propre transaction de voir que la transaction est exécutée de l'autre côté. +La fonction `approve` crée une allocation. Assurez-vous de lire le message sur +la façon dont elle peut être utilisée de manière abusive. Dans Ethereum, vous contrôlez l'ordre de vos propres transactions, +mais vous ne pouvez pas contrôler l'ordre dans lequel les transactions des autres personnes seront +exécutées, à moins que vous ne soumettiez pas votre propre transaction avant de voir que la +transaction de l'autre partie a eu lieu.   ```solidity /** - * @dev Déplace l``amount` des jetons du `sender` vers le `recipient` grâce au - * mécanisme d'allocation. l'`amount` est ensuite déduit de l' - * allocation de l'appelant. + * @dev Déplace un `montant` (`amount`) de jetons de `sender` vers `recipient` en utilisant le + * mécanisme d'allocation. Le `montant` (`amount`) est ensuite déduit de l'allocation + * de l'appelant. * * Renvoie une valeur booléenne indiquant si l'opération a réussi. * @@ -160,14 +195,14 @@ La fonction `approve` crée une autorisation. Veillez à lire le message expliqu function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); ``` -Enfin, `transferFrom` est utilisé par le client pour dépenser réellement l'allocation. +Enfin, `transferFrom` est utilisé par le dépensier pour dépenser réellement l'allocation.   ```solidity /** - * @dev Émis lorsque les jetons `value` sont déplacés d'un compte (`from`) vers + * @dev Émis lorsque des jetons d'une `valeur` (`value`) sont déplacés d'un compte (`from`) à * un autre (`to`). * * Notez que `value` peut être zéro. @@ -175,8 +210,8 @@ Enfin, `transferFrom` est utilisé par le client pour dépenser réellement l'al event Transfer(address indexed from, address indexed to, uint256 value); /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` est la nouvelle allocation. + * @dev Émis lorsque l'allocation d'un `dépensier` (`spender`) pour un `propriétaire` (`owner`) est définie par + * un appel à {approve}. `value` est la nouvelle allocation. */ event Approval(address indexed owner, address indexed spender, uint256 value); } @@ -186,7 +221,10 @@ Ces événements sont émis lorsque l'état du contrat ERC-20 change. ## Le contrat réel {#the-actual-contract} -Ceci est le contrat réel qui implémente la norme ERC-20, [tirée d'ici](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). Il n'est pas destiné à être utilisé tel quel, mais vous pouvez en [hériter](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm) pour l'étendre à quelque chose d'utilisable. +Ceci est le contrat réel qui implémente la norme ERC-20, +[tiré d'ici](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Il n'est pas destiné à être utilisé tel quel, mais vous pouvez +[en hériter](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm) pour l'étendre à quelque chose d'utilisable. ```solidity // SPDX-License-Identifier: MIT @@ -195,9 +233,9 @@ pragma solidity >=0.6.0 <0.8.0;   -### Importer les relevés {#import-statements} +### Instructions d'importation {#import-statements} -En complément des définitions d'interface ci-dessus, la définition de contrat importe deux autres fichiers : +En plus des définitions d'interface ci-dessus, la définition du contrat importe deux autres fichiers : ```solidity @@ -206,37 +244,42 @@ import "./IERC20.sol"; import "../../math/SafeMath.sol"; ``` -- `GSN/Context.sol` est l'ensemble des définitions requises pour utiliser [OpenGSN](https://www.opengsn.org/), un système qui permet aux utilisateurs sans ether d'utiliser la blockchain. Notez que c'est une ancienne version, si vous souhaitez une intégration avec OpenGSN [utilisez ce tutoriel](https://docs.opengsn.org/javascript-client/tutorial.html). -- [La bibliothèque SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/), qui est utilisée pour faire des ajouts et retraits sans dépassements. Ceci est nécessaire car sinon une personne pourrait avoir un jeton, dépenser deux jetons, puis avoir 2^256-1 jetons. +- `GSN/Context.sol` contient les définitions requises pour utiliser [OpenGSN](https://www.opengsn.org/), un système qui permet aux utilisateurs sans ether + d'utiliser la blockchain. Notez qu'il s'agit d'une ancienne version. Si vous voulez vous intégrer à OpenGSN, + [utilisez ce tutoriel](https://docs.opengsn.org/javascript-client/tutorial.html). +- [La bibliothèque SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/), qui empêche + les dépassements/sous-dépassements arithmétiques pour les versions de Solidity **<0.8.0**. Dans Solidity ≥0.8.0, les opérations arithmétiques + s'annulent automatiquement en cas de dépassement/sous-dépassement, ce qui rend SafeMath inutile. Ce contrat utilise SafeMath pour la compatibilité descendante avec + les anciennes versions du compilateur.   -Ce commentaire explique la finalité du contrat. +Ce commentaire explique l'objectif du contrat. ```solidity /** - * Implémentation @dev de l'interface {IERC20}. + * @dev Implémentation de l'interface {IERC20}. * - * Cette implémentation ne sait pas comment les jetons sont créés. Cela signifie - * qu'un mécanisme de génération doit être ajouté dans un contrat dérivé en utilisant {_mint}. + * Cette implémentation est agnostique quant à la manière dont les jetons sont créés. Cela signifie + * qu'un mécanisme d'approvisionnement doit être ajouté dans un contrat dérivé utilisant {_mint}. * Pour un mécanisme générique, voir {ERC20PresetMinterPauser}. * - * CONSEIL : Pour une description détaillée, consultez notre guide + * CONSEIL : pour un exposé détaillé, consultez notre guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[Comment * implémenter des mécanismes d'approvisionnement]. * - * Nous avons suivi les directives générales d'OpenZeppelin : les fonctions s'inversent au lieu de - * faire état d'un `false` en cas d'échec. Ce comportement est néanmoins classique + * Nous avons suivi les directives générales d'OpenZeppelin : les fonctions s'annulent au lieu de + * renvoyer « `false` » en cas d'échec. Ce comportement est néanmoins conventionnel * et n'entre pas en conflit avec les attentes des applications ERC20. * - * De plus, un événement {Approval} est émis en cas d'appels vers {transferFrom}. - * Cela permet aux applications de reconstituer l'allocation pour tous les comptes rien - * qu'en écoutant lesdits événements. Les autres implémentations de l'EIP peuvent ne pas émettre - * ces événements car la spécification ne le demande pas. + * De plus, un événement {Approval} est émis lors des appels à {transferFrom}. + * Cela permet aux applications de reconstruire l'allocation pour tous les comptes simplement + * en écoutant lesdits événements. D'autres implémentations de l'EIP peuvent ne pas émettre + * ces événements, car ce n'est pas requis par la spécification. * - * Enfin, les fonctions non normalisées {decreaseAllowance} et {increaseAllowance} - * ont été ajoutées pour atténuer les problèmes bien connus autour de la définition des - * allocations. Voir {IERC20-approve}. + * Enfin, les fonctions non standard {decreaseAllowance} et {increaseAllowance} + * ont été ajoutées pour atténuer les problèmes bien connus concernant la définition + * des allocations. Voir {IERC20-approve}. */ ``` @@ -247,7 +290,7 @@ Ce commentaire explique la finalité du contrat. contract ERC20 is Context, IERC20 { ``` -Cette ligne spécifie l'héritage : dans ce cas du `IERC20` ci-dessus et `Context`, pour OpenGSN. +Cette ligne spécifie l'héritage, dans ce cas de `IERC20` ci-dessus et de `Context`, pour OpenGSN.   @@ -257,19 +300,27 @@ Cette ligne spécifie l'héritage : dans ce cas du `IERC20` ci-dessus et `Contex ``` -Cette ligne associe la bibliothèque `SafeMath` au type `uint256`. Vous pouvez trouver cette bibliothèque [ici](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol). +Cette ligne attache la bibliothèque `SafeMath` au type `uint256`. Vous pouvez trouver cette bibliothèque +[ici](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol). ### Définitions des variables {#variable-definitions} -Ces définitions précisent les variables d'état du contrat. Il y a des variables déclarées `private`, mais cela signifie uniquement que d'autres contrats sur la blockchain ne peuvent pas les lire. _Il n'ey a rien de secret sur la blockchain_, le logiciel sur chaque nœud dispose de l'état de chaque contrat pour chaque bloc. Par convention, les variables d'état sont nommées `_`. +Ces définitions spécifient les variables d'état du contrat. Ces variables sont déclarées comme `private`, mais +cela signifie seulement que les autres contrats sur la blockchain ne peuvent pas les lire. _Il n'y a pas de +secrets sur la blockchain_, le logiciel sur chaque nœud a l'état de chaque contrat +à chaque bloc. Par convention, les variables d'état sont nommées `_`. -Les deux premières variables sont des [mappings](https://www.tutorialspoint.com/solidity/solidity_mappings.htm), ce qui signifie qu'elles se comportent à peu près comme un [tableau associatif](https://wikipedia.org/wiki/Associative_array), si ce n'est que les clés sont des valeurs numériques. Il n'y a de possibilité de stockage que pour les entrées qui ont des valeurs différentes de la valeur par défaut (zéro). +Les deux premières variables sont des [mappings](https://www.tutorialspoint.com/solidity/solidity_mappings.htm), +ce qui signifie qu'elles se comportent à peu près de la même manière que des [tableaux associatifs](https://wikipedia.org/wiki/Associative_array), +sauf que les clés sont des valeurs numériques. Le stockage n'est alloué que pour les entrées qui ont des valeurs différentes +de la valeur par défaut (zéro). ```solidity mapping (address => uint256) private _balances; ``` -Le premier mapping, `_balances`, correspond aux adresses et aux soldes respectifs de ce jeton. Pour accéder au solde, utilisez cette syntaxe : `_balances[
]`. +Le premier mapping, `_balances`, contient les adresses et leurs soldes respectifs de ce jeton. Pour accéder +au solde, utilisez cette syntaxe : `_balances[]`.   @@ -277,7 +328,9 @@ Le premier mapping, `_balances`, correspond aux adresses et aux soldes respectif mapping (address => mapping (address => uint256)) private _allowances; ``` -Cette variable, `_allowances`, stocke les allocations expliquées plus haut. Le premier index est le propriétaire des jetons, et le second est le contrat avec l'allocation. Pour accéder au montant que l'adresse A peut dépenser à partir du compte de l'adresse B, utilisez `_allowances[B][A]`. +Cette variable, `_allowances`, stocke les allocations expliquées précédemment. Le premier index est le propriétaire +des jetons, et le second est le contrat avec l'allocation. Pour accéder au montant que l'adresse A peut +dépenser à partir du compte de l'adresse B, utilisez `_allowances[B][A]`.   @@ -285,7 +338,7 @@ Cette variable, `_allowances`, stocke les allocations expliquées plus haut. Le uint256 private _totalSupply; ``` -Comme le nom le suggère, cette variable garde une trace de la quantité totale de jetons. +Comme son nom l'indique, cette variable assure le suivi de l'offre totale de jetons.   @@ -295,34 +348,44 @@ Comme le nom le suggère, cette variable garde une trace de la quantité totale uint8 private _decimals; ``` -Ces trois variables sont utilisées pour améliorer la lisibilité. Les noms des deux premières parlent d'eux-mêmes, mais pas `_decimals`. +Ces trois variables sont utilisées pour améliorer la lisibilité. Les deux premières sont explicites, mais `_decimals` +ne l'est pas. -D'une part, Ethereum n'a pas de variables à virgule flottante ou fractionnées. D'autre part, les gens apprécient de pouvoir diviser les jetons. Si les gens se sont orientés vers l'or comme monnaie c'est parce qu'il était difficile de faire de la monnaie quand quelqu'un voulait acheter la valeur d'un canard en vache. +D'une part, Ethereum ne dispose pas de variables à virgule flottante ou fractionnaires. D'autre part, +les humains aiment pouvoir diviser les jetons. Une des raisons pour lesquelles les gens ont opté pour l'or comme monnaie était qu'il +était difficile de rendre la monnaie lorsque quelqu'un voulait acheter l'équivalent d'un canard en vache. -La solution est de garder la trace des entiers, mais de compter à la place du jeton réel un jeton fractionné qui est presque sans valeur. Dans le cas de l'éther, la fraction de jeton est appelée wei, et 10^18 wei est égal à un ETH. A l’écriture, 10.000.000.000.000 wei représentent approximativement un centime de dollars américain ou un centime d’euro. +La solution consiste à garder la trace des nombres entiers, mais de compter, au lieu du jeton réel, un jeton fractionnaire qui n'a +pratiquement aucune valeur. Dans le cas de l'ether, le jeton fractionnaire est appelé wei, et 10^18 wei est égal à un +ETH. Au moment de la rédaction de cet article, 10 000 000 000 000 de wei valent environ un centime de dollar américain ou d'euro. -Les applications ont besoin de savoir comment afficher le solde de jetons. Si un utilisateur dispose de 3.141.000.000.000.000.000 wei, est-ce que cela correspond à 3,14 ETH ? 31,41 ETH ? 3,141 ETH ? Dans le cas de l'éther, il est défini comme 10^18 wei pour un ETH, mais pour votre jeton, vous pouvez sélectionner une valeur différente. Si diviser le jeton n'a pas de sens, vous pouvez définir une valeur `_decimals` de zéro. Si vous souhaitez definir le même standard que pour ETH, utilisez la valeur **18**. +Les applications doivent savoir comment afficher le solde de jetons. Si un utilisateur a 3 141 000 000 000 000 000 wei, est-ce que cela fait +3,14 ETH ? 31,41 ETH ? 3 141 ETH ? Dans le cas de l'ether, il est défini que 10^18 wei valent 1 ETH, mais pour votre +jeton, vous pouvez sélectionner une valeur différente. Si la division du jeton n'a pas de sens, vous pouvez utiliser une +valeur `_decimals` de zéro. Si vous voulez utiliser la même norme que l'ETH, utilisez la valeur **18**. -### Le Constructeur {#the-constructor} +### Le constructeur {#the-constructor} ```solidity /** - * @dev Définit les valeurs de {name} et {symbol}, initialise {decimals} avec + * @dev Définit les valeurs pour {name} et {symbol}, initialise {decimals} avec * une valeur par défaut de 18. * * Pour sélectionner une valeur différente pour {decimals}, utilisez {_setupDecimals}. * * Ces trois valeurs sont immuables : elles ne peuvent être définies qu'une seule fois pendant - * la phase de construction. + * la construction. */ constructor (string memory name_, string memory symbol_) public { + // Dans Solidity ≥0.7.0, 'public' est implicite et peut être omis. + _name = name_; _symbol = symbol_; _decimals = 18; } ``` -Le constructeur est appelé lorsque le contrat est créé pour la première fois. Par convention, les paramètres de fonction sont nommés `_`. +Le constructeur est appelé lors de la création initiale du contrat. Par convention, les paramètres de fonction sont nommés `_`. ### Fonctions de l'interface utilisateur {#user-interface-functions} @@ -335,24 +398,24 @@ Le constructeur est appelé lorsque le contrat est créé pour la première fois } /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. + * @dev Renvoie le symbole du jeton, généralement une version plus courte du + * nom. */ function symbol() public view returns (string memory) { return _symbol; } /** - * @dev Returns the number of decimals used to get its user representation. - * Par exemple, si `decimals` est égal à `2`, un solde de jetons `505` devrait - * être affiché comme suit pour un utilisateur : `5,05` (`505 / 10 ** 2`). + * @dev Renvoie le nombre de décimales utilisées pour obtenir sa représentation utilisateur. + * Par exemple, si `decimals` est égal à `2`, un solde de `505` jetons devrait + * être affiché à un utilisateur comme `5,05` (`505 / 10 ** 2`). * - * Les jetons optent généralement pour une valeur de 18, qui correspond à la relation entre - * éther et wei. C'est la valeur {ERC20} utilisée, sauf si {_setupDecimals} est - * appelé. + * Les jetons optent généralement pour une valeur de 18, imitant la relation entre + * l'ether et le wei. C'est la valeur que {ERC20} utilise, à moins que {_setupDecimals} ne soit + * appelée. * - * REMARQUE : Cette information n'est utilisée qu'à des fins _affichage_ : - * elle n'affecte en aucune façon l'arithmétique du contrat, y compris + * NOTE : Cette information n'est utilisée qu'à des fins d'_affichage_ : elle n'affecte + * en aucun cas l'arithmétique du contrat, y compris * {IERC20-balanceOf} et {IERC20-transfer}. */ function decimals() public view returns (uint8) { @@ -360,61 +423,67 @@ Le constructeur est appelé lorsque le contrat est créé pour la première fois } ``` -Ces fonctions, `name`, `symbol`, et `decimal` aident les interfaces utilisateur à connaître votre contrat afin qu'elles puissent l'afficher correctement. +Ces fonctions, `name`, `symbol` et `decimals` aident les interfaces utilisateur à connaître votre contrat afin qu'elles puissent l'afficher correctement. -Le type de retour est `string memory`, ce qui signifie retourner une chaîne de caractères stockée en mémoire. Les variables telles que les chaînes peuvent être stockées à trois endroits : +Le type de retour est `string memory`, ce qui signifie renvoyer une chaîne de caractères stockée en mémoire. Les variables, telles que les +chaînes de caractères, peuvent être stockées à trois endroits : -| | Durée de vie | Accès au contrat | Coût énergétique | -| --------------- | -------------------- | ---------------- | ------------------------------------------------------------------------------------------ | -| Mémoire | Appel de la fonction | Lecture/Écriture | Dix ou des centaines (plus pour des endroits plus élevés) | -| Données d'appel | Appel de la fonction | Lecture seule | Ne peut pas être utilisé comme type de retour, uniquement un type de paramètre de fonction | -| Stockage | Jusqu'au changement | Lecture/Écriture | Haut (800 pour la lecture, 20k pour l'écriture) | +| | Durée de vie | Accès au contrat | Coût du gaz | +| --------------- | -------------------- | ---------------- | -------------------------------------------------------------------------------------------- | +| Memory | Appel de fonction | Lecture/écriture | Dizaines ou centaines (plus élevé pour les emplacements plus élevés) | +| Données d'appel | Appel de fonction | Lecture seule | Ne peut pas être utilisé comme type de retour, seulement comme type de paramètre de fonction | +| Stockage | Jusqu'à modification | Lecture/écriture | Élevé (800 pour la lecture, 20k pour l'écriture) | Dans ce cas, `memory` est le meilleur choix. -### Lire les informations du jeton {#read-token-information} +### Lire les informations sur le jeton {#read-token-information} -Ce sont des fonctions qui fournissent des informations sur le jeton, soit l'offre totale, soit le solde d'un compte. +Ce sont des fonctions qui fournissent des informations sur le jeton, soit l'offre totale, soit le +solde d'un compte. ```solidity /** - * @dev See {IERC20-totalSupply}. + * @dev Voir {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } ``` -La fonction `totalSupply` fournit la quantité totale de jetons. +La fonction `totalSupply` renvoie l'offre totale de jetons.   ```solidity /** - * @dev See {IERC20-balanceOf}. + * @dev Voir {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } ``` -Lit le solde d'un compte. Notez que n'importe qui est autorisé à obtenir le solde du compte de n'importe qui d'autre. Il est inutile d'essayer de masquer cette information car de toute façon, elle est disponible sur tous les nœuds. _Il n'existe aucun secret sur la blockchain._ +Lire le solde d'un compte. Notez que n'importe qui est autorisé à obtenir le solde du compte +de n'importe qui d'autre. Il est inutile d'essayer de cacher cette information, car elle est de toute façon disponible sur chaque +nœud. _Il n'y a pas de secrets sur la blockchain._ ### Transférer des jetons {#transfer-tokens} ```solidity /** - * @dev See {IERC20-transfer}. + * @dev Voir {IERC20-transfer}. * - * Pré-requis: + * Exigences : * - * - le `bénéficiaire ' ne peut pas être l'adresse zéro. - * - l'appelant doit avoir un solde au moins égal au `montant`. + * - `recipient` ne peut pas être l'adresse zéro. + * - l'appelant doit avoir un solde d'au moins `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { ``` -La fonction `transfer` est appelée pour transférer des jetons depuis le compte de l'expéditeur vers un autre compte. Remarquez que même si elle fournit une valeur booléenne, cette valeur est toujours **true**. Si le transfert échoue, le contrat reprend l'appel. +La fonction `transfer` est appelée pour transférer des jetons du compte de l'expéditeur vers un autre. Notez +que même si elle renvoie une valeur booléenne, cette valeur est toujours **true**. Si le transfert +échoue, le contrat annule l'appel.   @@ -424,44 +493,51 @@ La fonction `transfer` est appelée pour transférer des jetons depuis le compte } ``` -La fonction `_transfer` fait le travail réel. C'est une fonction privée qui ne peut être appelée que par d'autres fonctions de contrat. Par convention les fonctions privées sont nommées `_`, comme les variables d'état. +La fonction `_transfer` fait le travail réel. C'est une fonction privée qui ne peut être appelée que par +d'autres fonctions du contrat. Par convention, les fonctions privées sont nommées `_`, comme les variables +d'état. -Habituellement avec Solidity nous utilisons `msg.sender` pour l'expéditeur du message. Cependant, cela casse [OpenGSN](http://opengsn.org/). Si nous voulons autoriser les transactions avec notre jeton, nous devons utiliser `_msgSender()`. Elle retournera `msg.sender` pour les transactions normales, en revanche pour les transactions sans ether elle indiquera le signataire original et non le contrat qui a relayé le message. +Normalement, dans Solidity, nous utilisons `msg.sender` pour l'expéditeur du message. Cependant, cela casse +[OpenGSN](http://opengsn.org/). Si nous voulons autoriser les transactions sans ether avec notre jeton, nous +devons utiliser `_msgSender()`. Elle renvoie `msg.sender` pour les transactions normales, mais pour celles sans ether, +elle renvoie le signataire original et non le contrat qui a relayé le message. -### Fonctions de provision {#allowance-functions} +### Fonctions d'allocation {#allowance-functions} -Ce sont les fonctions qui implémentent la fonctionnalité de provision : `allowance`, `approve`, `transferFrom`, et `_approve`. De plus, l'implémentation d'OpenZeppelin va au-delà du standard de base pour inclure certaines fonctionnalités qui améliorent la sécurité : `increaseAllowance`, et `decreaseAllowance`. +Ce sont les fonctions qui implémentent la fonctionnalité d'allocation : `allowance`, `approve`, `transferFrom`, +et `_approve`. De plus, l'implémentation d'OpenZeppelin va au-delà de la norme de base pour inclure certaines fonctionnalités qui améliorent la +sécurité : `increaseAllowance` et `decreaseAllowance`. #### La fonction d'allocation {#allowance} ```solidity /** - * @dev See {IERC20-allowance}. + * @dev Voir {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } ``` -La fonction `allowance` permet à quiconque de vérifier n'importe quelle allocation. +La fonction `allowance` permet à tout le monde de vérifier n'importe quelle allocation. #### La fonction d'approbation {#approve} ```solidity /** - * @dev See {IERC20-approve}. + * @dev Voir {IERC20-approve}. + * + * Exigences : * - * Exigences : -Pré-requis * * - `spender` ne peut pas être l'adresse zéro. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { ``` -Cette fonction est appelée pour créer une provision. Elle est similaire à la fonction `transfer` ci-dessus : +Cette fonction est appelée pour créer une allocation. Elle est similaire à la fonction `transfer` ci-dessus : - La fonction appelle simplement une fonction interne (dans ce cas, `_approve`) qui fait le travail réel. -- La fonction retourne soit `true` (si réussi) ou retour (si ce n'est pas le cas). +- La fonction renvoie soit `true` (en cas de succès), soit annule (sinon).   @@ -471,24 +547,26 @@ Cette fonction est appelée pour créer une provision. Elle est similaire à la } ``` -Nous utilisons des fonctions internes pour minimiser le nombre d'endroits où des changements d'état se produisent. _Toute_ fonction qui change l'état présente un risque potentiel de sécurité qui doit être audité pour déceler d'éventuelles failles de sécurité. De cette manière, nous avons moins de risques de nous tromper. +Nous utilisons des fonctions internes pour minimiser le nombre d'endroits où des changements d'état se produisent. Toute fonction qui modifie l'état +est un risque de sécurité potentiel qui doit être audité pour la sécurité. De cette façon, nous avons moins de chances de nous tromper. -#### Fonction transferFrom {#transferFrom} +#### La fonction transferFrom {#transferFrom} -C'est la fonction que le client appelle pour dépenser une provision. Cela nécessite deux opérations : transférer le montant dépensé et réduire d'autant le montant de la provision. +C'est la fonction qu'un dépensier appelle pour dépenser une allocation. Cela nécessite deux opérations : transférer le montant +dépensé et réduire l'allocation de ce montant. ```solidity /** - * @dev See {IERC20-transferFrom}. + * @dev Voir {IERC20-transferFrom}. * - * Émet un événement {Approval} indiquant le montant de la provision mis à jour. This is not - * required by the EIP. Voir la note au début de {ERC20}. + * Émet un événement {Approval} indiquant l'allocation mise à jour. Ceci n'est pas + * requis par l'EIP. Voir la note au début de {ERC20}. * - * Pré-requis : - * - * - `spender` et `recipient` ne peuvent pas correspondre à l'adresse zéro. + * Exigences : + * + * - `sender` et `recipient` ne peuvent pas être l'adresse zéro. * - `sender` doit avoir un solde d'au moins `amount`. - * - l'appelant doit avoir une allocation pour les jetons du ``sender`` d'au moins + * - l'appelant doit avoir une allocation pour les jetons de `sender` d'au moins * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual @@ -498,7 +576,9 @@ C'est la fonction que le client appelle pour dépenser une provision. Cela néce   -L'appel à la fonction `a.sub(b, "message")` fait deux choses. Tout d'abord, elle calcule `a-b`, qui est la nouvelle provision. Deuxièmement, elle vérifie que ce résultat n'est pas négatif. S'il est négatif, l'appel revient avec le message fourni. Veuillez noter que lorsqu'un appel annule tout traitement effectué précédemment pendant cet appel il est ignoré pour que nous n'ayons pas besoin d'annuler le `_transfer`. +L'appel de fonction `a.sub(b, "message")` fait deux choses. Premièrement, il calcule `a-b`, qui est la nouvelle allocation. +Deuxièmement, il vérifie que ce résultat n'est pas négatif. S'il est négatif, l'appel s'annule avec le message fourni. Notez que lorsqu'un appel s'annule, tout traitement effectué précédemment pendant cet appel est ignoré, nous n'avons donc pas besoin d'annuler +le `_transfer`. ```solidity _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, @@ -507,48 +587,60 @@ L'appel à la fonction `a.sub(b, "message")` fait deux choses. Tout d'abord, ell } ``` -#### Ajouts de sécurité OpenZeppelin {#openzeppelin-safety-additions} +#### Ajouts de sécurité d'OpenZeppelin {#openzeppelin-safety-additions} -Il est dangereux d'attribuer à une provision non nulle une autre valeur non nulle parce que vous ne contrôlez que l'ordre de vos propres transactions, pas celles de quelqu'un d'autre. Imaginez que vous ayez deux utilisateurs, Alice qui est naïve et Bill qui est malhonnête. Alice souhaite solliciter un service de la part de Bill qui, selon elle, coûte cinq jetons - donc elle donne à Bill une provision de cinq jetons. +Il est dangereux de définir une allocation non nulle sur une autre valeur non nulle, +car vous ne contrôlez que l'ordre de vos propres transactions, pas celles de quelqu'un d'autre. Imaginez que vous +ayez deux utilisateurs, Alice qui est naïve et Bill qui est malhonnête. Alice veut un service de la part de +Bill, qui, selon elle, coûte cinq jetons. Elle donne donc à Bill une allocation de cinq jetons. -Puis, quelque chose change et le prix de Bill monte à dix jetons. Alice, qui souhaite toujours le service, envoie une transaction qui fixe la provision de Bill à dix. Au moment où Bill voit cette nouvelle transaction dans le pool de transactions, il envoie une transaction qui dépense les cinq jetons d'Alice et a un coût énergétique bien plus élevé, donc elle sera épuisée plus rapidement. De cette façon, Bill peut dépenser les cinq premiers jetons puis, une fois que la nouvelle provision d'Alice est épuisée, en dépenser dix de plus pour un prix total de quinze jetons, plus qu'Alice est censée avoir autorisé. Cette technique est appelée [front-running](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running) +Puis, quelque chose change et le prix de Bill passe à dix jetons. Alice, qui veut toujours le service, +envoie une transaction qui définit l'allocation de Bill à dix. Dès que Bill voit cette nouvelle transaction +dans le pool de transactions, il envoie une transaction qui dépense les cinq jetons d'Alice et a un prix de +gaz beaucoup plus élevé pour qu'elle soit minée plus rapidement. De cette façon, Bill peut d'abord dépenser cinq jetons, puis, +une fois que la nouvelle allocation d'Alice est minée, en dépenser dix de plus pour un prix total de quinze jetons, plus que ce qu'Alice +voulait autoriser. Cette technique est appelée le +[front-running](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running) -| Transaction d'Alice | Nonce d'Alice | Transaction de Bill | Nonce de Bill | Provision de Bill | Total facturé par Bill à Alice | -| ------------------- | ------------- | ----------------------------- | ------------- | ----------------- | ------------------------------ | -| approve(Bill, 5) | 10 | | | 5 | 0 | -| | | transferFrom(Alice, Bill, 5) | 10 123 | 0 | 5 | -| approve(Bill, 10) | 11 | | | 10 | 5 | -| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | +| Transaction d'Alice | Nonce d'Alice | Transaction de Bill | Nonce de Bill | Allocation de Bill | Revenu total de Bill provenant d'Alice | +| ------------------------------------ | ------------- | ------------------------------------------------ | ------------- | ------------------ | -------------------------------------- | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| approve(Bill, 10) | 11 | | | 10 | 5 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | -Pour éviter ce problème, ces deux fonctions (`increaseAllowance` et `decreaseAllowance`) vous permettent de modifier la provision d'un montant spécifique. Ainsi, si Bill a déjà dépensé cinq jetons, il ne sera autorisé à dépenser que cinq jetons de plus. En fonction du timing, il y a deux façons de procéder, les deux aboutissant au fait que Bill n'obtient que dix jetons : +Pour éviter ce problème, ces deux fonctions (`increaseAllowance` et `decreaseAllowance`) vous permettent +de modifier l'allocation d'un montant spécifique. Donc, si Bill a déjà dépensé cinq jetons, il ne +pourra en dépenser que cinq de plus. Selon le moment, il y a deux façons dont cela peut fonctionner, les deux se terminant +avec Bill n'obtenant que dix jetons : A : -| Transaction d'Alice | Nonce d'Alice | Transaction de Bill | Nonce de Bill | Provision de Bill | Total facturé par Bill à Alice | -| -------------------------- | -------------:| ---------------------------- | -------------:| -----------------:| ------------------------------ | -| approve(Bill, 5) | 10 | | | 5 | 0 | -| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | -| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | -| | | transferFrom(Alice, Bill, 5) | 10 124 | 0 | 10 | +| Transaction d'Alice | Nonce d'Alice | Transaction de Bill | Nonce de Bill | Allocation de Bill | Revenu total de Bill provenant d'Alice | +| --------------------------------------------- | ------------: | ----------------------------------------------- | ------------: | -----------------: | -------------------------------------- | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | +| | | transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 | B : -| Transaction d'Alice | Nonce d'Alice | Transaction de Bill | Nonce de Bill | Provision de Bill | Total facturé par Bill à Alice | -| -------------------------- | -------------:| ----------------------------- | -------------:| -----------------:| ------------------------------:| -| approve(Bill, 5) | 10 | | | 5 | 0 | -| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | -| | | transferFrom(Alice, Bill, 10) | 10 124 | 0 | 10 | +| Transaction d'Alice | Nonce d'Alice | Transaction de Bill | Nonce de Bill | Allocation de Bill | Revenu total de Bill provenant d'Alice | +| --------------------------------------------- | ------------: | ------------------------------------------------ | ------------: | -----------------: | -------------------------------------: | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 | ```solidity /** - * @dev augmente atomiquement l'allocation accordée à `spender` par l'appelant. + * @dev Augmente atomiquement l'allocation accordée à `spender` par l'appelant. * - * C'est une alternative à {approve} qui peut être utilisée pour atténuer les + * Il s'agit d'une alternative à {approve} qui peut être utilisée comme atténuation des * problèmes décrits dans {IERC20-approve}. * - * Émet un événement {Approval} indiquant l'allocation telle que mise à jour. + * Émet un événement {Approval} indiquant l'allocation mise à jour. * - * Pré-requis : + * Exigences : * * - `spender` ne peut pas être l'adresse zéro. */ @@ -558,22 +650,23 @@ B : } ``` -La fonction `a.add(b)` est un ajout sûr. Dans le cas peu probable où `a`+`b`>=`2^256` il n'intègre pas la manière dont l'ajout normal doit se réaliser. +La fonction `a.add(b)` est une addition sûre. Dans le cas peu probable où `a`+`b`>=`2^256`, elle ne boucle pas +comme le fait une addition normale. ```solidity /** - * @dev diminue atomiquement l'allocation accordée par l'appelant à `spender`. + * @dev Diminue atomiquement l'allocation accordée à `spender` par l'appelant. * - * C'est une alternative à {approve} qui peut être utilisée pour atténuer les + * Il s'agit d'une alternative à {approve} qui peut être utilisée comme atténuation des * problèmes décrits dans {IERC20-approve}. * - * Émet un événement {Approval} indiquant le montant actualisé de l'allocation. + * Émet un événement {Approval} indiquant l'allocation mise à jour. * - * Pré-requis : + * Exigences : * * - `spender` ne peut pas être l'adresse zéro. - * - `spender` doit avoir une provision pour l'appelant d'au moins + * - `spender` doit avoir une allocation pour l'appelant d'au moins * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { @@ -583,22 +676,22 @@ La fonction `a.add(b)` est un ajout sûr. Dans le cas peu probable où `a`+`b`>= } ``` -### Fonctions qui modifient les informations du jeton {#functions-that-modify-token-information} +### Fonctions qui modifient les informations sur les jetons {#functions-that-modify-token-information} -Voici les quatre fonctions qui font le travail réel : `_transfer`, `_mint`, `_burn`, et `_approve`. +Ce sont les quatre fonctions qui font le travail réel : `_transfer`, `_mint`, `_burn` et `_approve`. -#### La fonction \_transfer {#_transfer} +#### La fonction _transfer {#_transfer} ```solidity /** - * @dev Déplace les jetons `amount` de `sender` à `recipient`. + * @dev Déplace un `montant` (`amount`) de jetons de `sender` à `recipient`. * - * Cette fonction interne équivaut à {transfer}, et peut être utilisée pour - * par ex. définir des frais de jetons automatiques, des mécanismes de réduction, etc. + * Cette fonction interne est équivalente à {transfer}, et peut être utilisée pour + * par exemple, implémenter des frais de jeton automatiques, des mécanismes de délestage, etc. * * Émet un événement {Transfer}. * - * Pré-requis : + * Exigences : * * - `sender` ne peut pas être l'adresse zéro. * - `recipient` ne peut pas être l'adresse zéro. @@ -607,7 +700,9 @@ Voici les quatre fonctions qui font le travail réel : `_transfer`, `_mint`, `_b function _transfer(address sender, address recipient, uint256 amount) internal virtual { ``` -Cette fonction, `_transfer`, transfère des jetons d'un compte à un autre. Elle est appelée à la fois par `transfer` (pour les transferts depuis le compte de l'expéditeur) et `transferFrom` (pour utiliser les provisions à être transférée depuis le compte de quelqu'un d'autre). +Cette fonction, `_transfer`, transfère des jetons d'un compte à un autre. Elle est appelée à la fois par +`transfer` (pour les transferts depuis le propre compte de l'expéditeur) et `transferFrom` (pour utiliser des allocations +pour transférer depuis le compte de quelqu'un d'autre).   @@ -616,7 +711,9 @@ Cette fonction, `_transfer`, transfère des jetons d'un compte à un autre. Elle require(recipient != address(0), "ERC20: transfer to the zero address"); ``` -Personne ne possède réellement l'adresse zéro dans Ethereum (c'est-à-dire que personne ne connaît une clé privée dont la clé publique correspondante est transformée en une adresse zéro). Lorsque les personnes utilisent cette adresse, il s'agit généralement d'un bogue logiciel - donc nous échouons si l'adresse zéro est utilisée comme expéditeur ou destinataire. +Personne ne possède réellement l'adresse zéro dans Ethereum (c'est-à-dire que personne ne connaît une clé privée dont la clé publique correspondante +est transformée en adresse zéro). Lorsque les gens utilisent cette adresse, il s'agit généralement d'un bogue logiciel. Nous échouons donc +si l'adresse zéro est utilisée comme expéditeur ou destinataire.   @@ -627,12 +724,15 @@ Personne ne possède réellement l'adresse zéro dans Ethereum (c'est-à-dire qu Il existe deux façons d'utiliser ce contrat : -1. Utilisez-le comme modèle pour votre propre code -1. [Héritez-en](https://www.bitdegree.org/learn/solidity-inheritance), et remplacez uniquement les fonctions que vous devez modifier +1. L'utiliser comme modèle pour votre propre code +2. [En hériter](https://www.bitdegree.org/learn/solidity-inheritance), et ne remplacer que les fonctions que vous devez modifier -La seconde méthode est bien meilleure parce que le code ERC-20 d'OpenZeppelin a déjà été audité et s'avère sécurisé. Lorsque vous utilisez la méthode d'héritage, il est facile de distinguer quelles sont les fonctions que vous avez modifiées et ainsi pour faire confiance à vos contrats, les personnes n'ont besoin que de vérifier ces fonctions spécifiques. +La deuxième méthode est bien meilleure car le code ERC-20 d'OpenZeppelin a déjà été audité et s'est avéré sécurisé. Lorsque vous utilisez l'héritage, +les fonctions que vous modifiez sont claires, et pour faire confiance à votre contrat, les gens n'ont besoin que d'auditer ces fonctions spécifiques. -Il est souvent utile d'exécuter une fonction chaque fois que des jetons changent de main. Cependant, `_transfer` est une fonction très importante et il est possible de l'écrire de manière non sécurisée (voir ci-dessous), Il est donc préférable de ne pas le remplacer. La solution est `_beforeTokenTransfer`, une fonction [hook](https://wikipedia.org/wiki/Hooking). Vous pouvez remplacer cette fonction et elle sera appelée pour chaque transfert. +Il est souvent utile d'exécuter une fonction chaque fois que des jetons changent de mains. Cependant, `_transfer` est une fonction très importante et il est +possible de l'écrire de manière non sécurisée (voir ci-dessous), il est donc préférable de ne pas la remplacer. La solution est `_beforeTokenTransfer`, une +[fonction hook](https://wikipedia.org/wiki/Hooking). Vous pouvez remplacer cette fonction, et elle sera appelée à chaque transfert.   @@ -641,7 +741,10 @@ Il est souvent utile d'exécuter une fonction chaque fois que des jetons changen _balances[recipient] = _balances[recipient].add(amount); ``` -Ce sont les lignes qui exécutent réellement le transfert. Notez qu'il n'y a **rien** entre elles et que nous soustrayons le montant transféré du solde de l'expéditeur avant de l'ajouter au destinataire. Ceci est important car s'il y a eu entre temps un appel à un contrat différent, il aurait pu être utilisé pour abuser de ce contrat. De cette façon, le transfert est atomique, rien ne peut se produire en cours de route. +Ce sont les lignes qui effectuent réellement le transfert. Notez qu'il n'y a **rien** entre elles, et que nous soustrayons +le montant transféré de l'expéditeur avant de l'ajouter au destinataire. C'est important car s'il y avait un +appel à un contrat différent entre les deux, il aurait pu être utilisé pour tromper ce contrat. De cette façon, le transfert +est atomique, rien ne peut se produire au milieu de celui-ci.   @@ -650,23 +753,32 @@ Ce sont les lignes qui exécutent réellement le transfert. Notez qu'il n'y a ** } ``` -Enfin, émettre un événement `Transfert`. Les événements ne sont pas accessibles par les contrats intelligents, mais le code exécuté en dehors de la blockchain peut lire les événements et réagir. Par exemple, un portefeuille peut garder une trace du moment où le propriétaire obtient plus de jetons. +Enfin, émettre un événement `Transfer`. Les événements ne sont pas accessibles aux contrats intelligents, mais le code s'exécutant en dehors de la blockchain +peut écouter les événements et y réagir. Par exemple, un portefeuille peut suivre le moment où le propriétaire obtient plus de jetons. -#### La fonction \_mint and \_burn {#_mint-and-_burn} +#### Les fonctions _mint et _burn {#_mint-and-_burn} -Ces deux fonctions (`_mint` et `_burn`) modifient la quantité totale de jetons. Elles sont internes et il n'y a pas de fonction qui les appelle dans ce contrat, ainsi elles ne sont utiles que si vous héritez du contrat et ajoutez votre propre logique pour décider dans quelles conditions générer de nouveaux jetons ou utiliser les jetons existants. +Ces deux fonctions (`_mint` et `_burn`) modifient l'offre totale de jetons. +Elles sont internes et aucune fonction ne les appelle dans ce contrat, +elles ne sont donc utiles que si vous héritez du contrat et ajoutez votre propre +logique pour décider dans quelles conditions frapper de nouveaux jetons ou brûler des jetons +existants. -**REMARQUE :** Chaque jeton ERC-20 a sa propre logique commerciale qui dicte la gestion des jetons. Par exemple, un contrat d'approvisionnement fixe ne peut appeler que `_mint` dans le constructeur et jamais `_burn`. Un contrat qui vend des jetons appellera `_mint` lorsqu'il sera payé, et probablement `_burn` à un certain point pour éviter une inflation galopante. +**NOTE :** Chaque jeton ERC-20 a sa propre logique métier qui dicte la gestion des jetons. +Par exemple, un contrat à offre fixe peut n'appeler `_mint` +que dans le constructeur et ne jamais appeler `_burn`. Un contrat qui vend des jetons +appellera `_mint` lorsqu'il sera payé, et appellera vraisemblablement `_burn` à un certain moment +pour éviter une inflation galopante. ```solidity - /** @dev Crée des jetons `amount` et les affecte à `account`, augmentant ainsi + /** @dev Crée un `montant` (`amount`) de jetons et les assigne à un `compte` (`account`), augmentant * l'offre totale. * - * Émet un événement {Transfer} avec `from` défini à l'adresse zéro. + * Émet un événement {Transfer} avec `from` défini sur l'adresse zéro. * - * Pré-requis: + * Exigences : * - * - `to' ne peut pas être l'adresse zéro. + * - `to` ne peut pas être l'adresse zéro. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); @@ -677,21 +789,21 @@ Ces deux fonctions (`_mint` et `_burn`) modifient la quantité totale de jetons. } ``` -Veillez à mettre à jour `_totalSupply` lorsque le nombre total de jetons change. +Assurez-vous de mettre à jour `_totalSupply` lorsque le nombre total de jetons change.   -``` +```solidity /** - * @dev Détruit les jetons `amount` de l'account`, réduisant ainsi + * @dev Détruit un `montant` (`amount`) de jetons du `compte` (`account`), réduisant * l'offre totale. * - * Émet un événement {Transfer} avec `to` défini à l'adresse zéro. + * Émet un événement {Transfer} avec `to` défini sur l'adresse zéro. * - * Pré-requis: + * Exigences : * - * - `account' ne peut pas être l'adresse zéro. - * - `account` doit au moins avoir un nombre de jetons correspondant à l'`amount`. + * - `account` ne peut pas être l'adresse zéro. + * - `account` doit avoir au moins un `montant` (`amount`) de jetons. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); @@ -704,22 +816,25 @@ Veillez à mettre à jour `_totalSupply` lorsque le nombre total de jetons chang } ``` -La fonction `_burn` est presque identique à `_mint` sauf qu'elle fonctionne en sens inverse. +La fonction `_burn` est presque identique à `_mint`, sauf qu'elle va dans l'autre sens. -#### Fonction \_approve {#_approve} +#### La fonction _approve {#_approve} -C'est la fonction qui spécifie les provisions. Notez qu'elle permet à un propriétaire de spécifier une provision supérieure au solde actuel du propriétaire. Cela ne pose pas de problème car le solde est vérifié au moment du transfert dans la mesure où il pourrait être différent du solde existant au moment de la création de la provision. +C'est la fonction qui spécifie réellement les allocations. Notez qu'elle permet à un propriétaire de spécifier +une allocation supérieure au solde actuel du propriétaire. Ce n'est pas un problème car le solde est +vérifié au moment du transfert, alors qu'il pourrait être différent du solde au moment de la création de l'allocation +. ```solidity /** - * @dev Définit le `montant` comme étant l'allocation que le `spender` attribue aux jetons de l'`owner`. + * @dev Définit un `montant` (`amount`) comme allocation de `spender` sur les jetons de `owner`. * * Cette fonction interne est équivalente à `approve`, et peut être utilisée pour - * par ex. définir des allocations automatiques pour certains sous-systèmes, etc. + * par exemple, définir des allocations automatiques pour certains sous-systèmes, etc. * * Émet un événement {Approval}. * - * Pré-requis : + * Exigences : * * - `owner` ne peut pas être l'adresse zéro. * - `spender` ne peut pas être l'adresse zéro. @@ -733,7 +848,8 @@ C'est la fonction qui spécifie les provisions. Notez qu'elle permet à un propr   -Émettre un événement `Approval`. Selon la façon dont l'application est écrite, le contrat du client peut être informé de l'approbation soit par le propriétaire, soit par un serveur qui lit ces événements. +Émettre un événement `Approval`. Selon la façon dont l'application est écrite, le contrat du dépensier peut être informé de l'approbation +soit par le propriétaire, soit par un serveur qui écoute ces événements. ```solidity emit Approval(owner, spender, amount); @@ -747,50 +863,68 @@ C'est la fonction qui spécifie les provisions. Notez qu'elle permet à un propr /** - * @dev Définit {decimals} à une valeur autre que la valeur par défaut de 18. + * @dev Définit {decimals} sur une valeur autre que celle par défaut de 18. * - * AVERTISSEMENT : Cette fonction ne doit être appelée qu'à partir du constructeur. La plupart des - * applications qui interagissent avec des contrats de jetons ne s'attendent pas à ce que - * {decimals} change jamais, et peuvent mal fonctionner si c'est le cas. + * AVERTISSEMENT : Cette fonction ne doit être appelée que depuis le constructeur. La plupart des + * applications qui interagissent avec les contrats de jeton ne s'attendront pas à ce que + * {decimals} change, et pourraient fonctionner de manière incorrecte si c'est le cas. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } ``` -Cette fonction modifie la variable `_decimals` qui est utilisée pour dicter aux interfaces utilisateur comment interpréter le montant. Vous devez l'appeler depuis le constructeur. Il serait malhonnête de l'appeler à n'importe quel moment ultérieur et les applications ne sont pas conçues pour le gérer. +Cette fonction modifie la variable `_decimals` qui est utilisée pour indiquer aux interfaces utilisateur comment interpréter le montant. +Vous devez l'appeler depuis le constructeur. Il serait malhonnête de l'appeler à un moment ultérieur, et les applications +ne sont pas conçues pour le gérer. -### Hooks {#hooks} +### Crochets {#hooks} ```solidity /** - * @dev Hook appelé avant tout transfert de jetons. * frappe et brûlage compris. + * @dev Hook appelé avant tout transfert de jetons. Cela inclut + * la frappe et la brûlure. * * Conditions d'appel : * - * - lorsque `from` et `to` ne sont pas à zéro, `amount` jetons de ``from`` - * seront transférés à `to`. - * - lorsque `from` est zéro, les jetons `amount` seront frappés pour `to`. - * - lorsque `to` est zéro, `amount` jetons de ``from`` seront brûlés. + * - lorsque `from` et `to` sont tous deux non nuls, un `montant` de jetons de `from` + * sera transféré à `to`. + * - lorsque `from` est zéro, un `montant` de jetons sera frappé pour `to`. + * - lorsque `to` est zéro, un `montant` de jetons de `from` sera brûlé. * - `from` et `to` ne sont jamais tous les deux nuls. * - * Pour en savoir plus sur les hook, rendez-vous sur xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + * Pour en savoir plus sur les hooks, rendez-vous sur xref:ROOT:extending-contracts.adoc#using-hooks[Utilisation des hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } ``` -Il s'agit de la fonction hook à appeler pendant les transferts. Elle est ici vide, mais si vous en avez besoin pour accomplir quelque chose, vous avez juste à la remplacer. +Il s'agit de la fonction hook à appeler pendant les transferts. Elle est vide ici, mais si vous avez besoin +qu'elle fasse quelque chose, il vous suffit de la remplacer. ## Conclusion {#conclusion} -Pour résumer, voici quelques-unes des idées les plus importantes de ce contrat (selon moi et les vôtres pourraient ne pas être les mêmes) : - -- _Il n'y a pas de secret sur la blockchain._ Toute information accessible par un contrat intelligent l'est pour le monde entier. -- Vous pouvez contrôler l'ordre de vos propres transactions, mais pas lorsque les transactions d'autres personnes sont en cours. C'est la raison pour laquelle un changement de provision peut être dangereux car il permet à la personne qui dépense de dépenser la somme des deux provisions. -- Valeurs de type `uint256` enveloppent autour. En d'autres termes, _0-1=2^256-1_. Si ce comportement n'est pas souhaité, vous devez le vérifier (ou utiliser la bibliothèque SafeMath qui le fera pour vous). Notez que cela a changé avec [Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html). -- Effectuez tous les changements d'état d'un type spécifique en un emplacement spécifique, car cela facilite la vérification. C'est la raison pour laquelle nous disposons par exemple de `_approve`, appelée par `approve` `transferFrom`, `increaseAllowance`, et `decreaseAllowance` -- Les changements d'état doivent être atomiques, sans aucune autre action au milieu (comme c'est le cas avec `_transfer`). Ceci parce que pendant le changement d'état, l'état est incohérent. Par exemple, entre le moment où vous déduisez du solde de la personne qui dépense et le moment où vous ajoutez au solde du bénéficiaire il y a moins de jeton existants qu'il ne devrait y en avoir. Ce laps de temps pourrait être utilisé à mauvais escient si des opérations interviennent entre eux, en particulier des appels à un contrat différent. - -Maintenant que vous avez pu constater comment le contrat OpenZeppelin ERC-20 est rédigé et surtout comment il est rendu plus sûr, rédigez vos propres contrats et applications sécurisés. +Pour résumer, voici quelques-unes des idées les plus importantes de ce contrat (à mon avis, le vôtre est susceptible de varier) : + +- _Il n'y a pas de secret sur la blockchain_. Toute information à laquelle un contrat intelligent peut accéder + est disponible pour le monde entier. +- Vous pouvez contrôler l'ordre de vos propres transactions, mais pas quand les transactions d'autres personnes + ont lieu. C'est la raison pour laquelle la modification d'une allocation peut être dangereuse, car elle permet + au dépensier de dépenser la somme des deux allocations. +- Les valeurs de type `uint256` se bouclent. En d'autres termes, _0-1=2^256-1_. Si ce n'est pas le comportement souhaité, + vous devez le vérifier (ou utiliser la bibliothèque SafeMath qui le fait pour vous). Notez que cela a changé dans + [Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html). +- Effectuez tous les changements d'état d'un type spécifique à un endroit spécifique, car cela facilite l'audit. + C'est la raison pour laquelle nous avons, par exemple, `_approve`, qui est appelé par `approve`, `transferFrom`, + `increaseAllowance` et `decreaseAllowance` +- Les changements d'état doivent être atomiques, sans aucune autre action au milieu (comme vous pouvez le voir + dans `_transfer`). C'est parce que pendant le changement d'état, vous avez un état incohérent. Par exemple, + entre le moment où vous déduisez du solde de l'expéditeur et le moment où vous ajoutez au solde du + destinataire, il y a moins de jetons en circulation qu'il ne devrait y en avoir. Cela pourrait être potentiellement exploité s'il y a + des opérations entre eux, en particulier des appels à un contrat différent. + +Maintenant que vous avez vu comment le contrat OpenZeppelin ERC-20 est écrit, et surtout comment il est +rendu plus sécurisé, allez écrire vos propres contrats et applications sécurisés. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/erc20-with-safety-rails/index.md b/public/content/translations/fr/developers/tutorials/erc20-with-safety-rails/index.md index 28edfe029b7..b36c194adf2 100644 --- a/public/content/translations/fr/developers/tutorials/erc20-with-safety-rails/index.md +++ b/public/content/translations/fr/developers/tutorials/erc20-with-safety-rails/index.md @@ -1,62 +1,64 @@ --- -title: ERC-20 en sécurité -description: Comment aider les gens à éviter des erreurs bêtes +title: ERC-20 avec des garde-fous +description: "Comment aider les gens à éviter des erreurs bêtes" author: Ori Pomerantz lang: fr -tags: - - "erc-20" +tags: [ "erc-20" ] skill: beginner published: 2022-08-15 --- ## Introduction {#introduction} -L'un des grands avantages avec Ethereum est qu'il n'y a pas d'autorité centrale qui peut modifier ou annuler vos transactions. L'un des grands problèmes avec Ethereum est qu'il n'y a pas d'autorité centrale ayant le pouvoir d'annuler les erreurs des utilisateurs ou les transactions illicites. Dans cet article, vous apprendrez quelques-unes des erreurs courantes que commettent les utilisateurs avec les jetons [ERC-20](/developers/docs/standards/tokens/erc-20/), ainsi que comment créer des contrats ERC-20 qui aident les utilisateurs à éviter ces erreurs, ou qui donnent à une autorité centrale certains pouvoirs (par exemple, geler des comptes). +L'un des grands avantages d'Ethereum est qu'il n'y a pas d'autorité centrale qui peut modifier ou annuler vos transactions. L'un des grands problèmes d'Ethereum est qu'il n'y a pas d'autorité centrale ayant le pouvoir d'annuler les erreurs des utilisateurs ou les transactions illicites. Dans cet article, vous apprendrez quelques-unes des erreurs courantes que commettent les utilisateurs avec les jetons [ERC-20](/developers/docs/standards/tokens/erc-20/), ainsi que comment créer des contrats ERC-20 qui aident les utilisateurs à éviter ces erreurs, ou qui donnent à une autorité centrale certains pouvoirs (par exemple, geler des comptes). -Notez que bien que nous utiliserons le [contrat de jeton ERC-20 d'OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20), cet article n'explique pas en détail son fonctionnement. Vous pouvez trouver ces informations [ici](/developers/tutorials/erc20-annotated-code). +Notez que bien que nous utilisions le [contrat de jeton ERC-20 d'OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20), cet article ne l'explique pas en détail. Vous pouvez trouver ces informations [ici](/developers/tutorials/erc20-annotated-code). Si vous souhaitez consulter le code source complet : 1. Ouvrez l'[IDE Remix](https://remix.ethereum.org/). -2. Cliquez sur l'icône de clonage GitHub (![clone GitHub icon](icon-clone.png)). -3. Clonez le référentiel GitHub `https://github.com/qbzzt/20220815-erc20-safety-rails`. -4. Ouvrez **contrats > erc20-safety-rails.sol**. +2. Cliquez sur l'icône de clonage de GitHub (![icône de clonage de GitHub](icon-clone.png)). +3. Clonez le dépôt GitHub `https://github.com/qbzzt/20220815-erc20-safety-rails`. +4. Ouvrez **contracts > erc20-safety-rails.sol**. ## Création d'un contrat ERC-20 {#creating-an-erc-20-contract} -Avant de pouvoir ajouter la fonctionnalité de sécurité, nous avons besoin d'un contrat ERC-20. Dans cet article, nous utiliserons [l'assistant de contrats OpenZeppelin](https://docs.openzeppelin.com/contracts/5.x/wizard). Ouvrez-le dans un autre navigateur et suivez ces instructions : +Avant de pouvoir ajouter la fonctionnalité de garde-fou, nous avons besoin d'un contrat ERC-20. Dans cet article, nous utiliserons [l'assistant de contrats OpenZeppelin](https://docs.openzeppelin.com/contracts/5.x/wizard). Ouvrez-le dans un autre navigateur et suivez ces instructions : 1. Sélectionnez **ERC20**. + 2. Entrez ces paramètres : | Paramètre | Valeur | | ---------------- | ---------------- | | Nom | SafetyRailsToken | | Symbole | SAFE | - | Pré-génère | 1000 | - | Fonctionnalités | Aucune | - | Contrôle d'accès | Propriétaire | - | Mise à jour | Aucune | + | Prémint | 1000 | + | Fonctionnalités | Aucun | + | Contrôle d'accès | Ownable | + | Évolutivité | Aucun | + +3. Faites défiler vers le haut et cliquez sur **Ouvrir dans Remix** (pour Remix) ou sur **Télécharger** pour utiliser un autre environnement. Je vais supposer que vous utilisez Remix. Si vous utilisez autre chose, faites simplement les modifications appropriées. -3. Remontez et cliquez sur **Ouvrir dans Remix** (pour Remix) ou **Télécharger** pour utiliser un environnement différent. Je vais supposer que vous utilisez Remix, si vous utilisez autre chose, faites simplement les modifications appropriées. -4. Nous avons maintenant un contrat ERC-20 pleinement fonctionnel. Vous pouvez développer `.deps` > ` npm` pour voir le code importé. -5. Compilez, déployez et jouez avec le contrat pour voir qu'il fonctionne comme un contrat ERC-20. Si vous devez apprendre à utiliser Remix, [utilisez ce tutoriel](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth). +4. Nous avons maintenant un contrat ERC-20 pleinement fonctionnel. Vous pouvez développer `.deps` > `npm` pour voir le code importé. + +5. Compilez, déployez et interagissez avec le contrat pour voir qu'il fonctionne comme un contrat ERC-20. Si vous avez besoin d'apprendre à utiliser Remix, [consultez ce tutoriel](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth). ## Erreurs courantes {#common-mistakes} ### Les erreurs {#the-mistakes} -Les utilisateurs envoient parfois des tokens à la mauvaise adresse. Bien que nous ne puissions pas lire dans leurs pensées pour savoir ce qu'ils voulaient faire, il existe deux types d'erreurs qui se produisent souvent et sont faciles à détecter : +Les utilisateurs envoient parfois des jetons à la mauvaise adresse. Bien que nous ne puissions pas lire dans leurs pensées pour savoir ce qu'ils voulaient faire, il existe deux types d'erreurs qui se produisent souvent et qui sont faciles à détecter : -1. Envoyer les jetons à l'adresse du contrat lui-même. Par exemple, [le token OP d'Optimism](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c) a réussi à accumuler [plus de 120 000 tokens OP](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000042#tokentxns) en moins de deux mois. Cela représente une somme d'argent considérable que, vraisemblablement, des gens ont simplement perdue. +1. Envoi des jetons à la propre adresse du contrat. Par exemple, le [jeton OP d'Optimism](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c) a accumulé [plus de 120 000](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000042) jetons OP en moins de deux mois. Cela représente une somme d'argent considérable que, vraisemblablement, des personnes ont simplement perdue. -2. Envoyer les tokens à une adresse vide, une adresse qui ne correspond pas à [un compte possédé extérieurement](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) ou [à un contrat intelligent](/developers/docs/smart-contracts). Bien que je n'aie pas de statistiques sur la fréquence à laquelle cela se produit, [un incident aurait pu coûter 20 000 000 tokens](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595). +2. Envoi de jetons à une adresse vide, c'est-à-dire une adresse qui ne correspond ni à un [compte externe](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) ni à un [contrat intelligent](/developers/docs/smart-contracts). Bien que je n'aie pas de statistiques sur la fréquence à laquelle cela se produit, [un incident aurait pu coûter 20 000 000 de jetons](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595). -### Prévenir les transferts {#preventing-transfers} +### Prévention des transferts {#preventing-transfers} -Le contrat ERC-20 d'OpenZeppelin comprend [un crochet`, _beforeTokenTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368), qui est appelé avant qu'un token soit transféré. Par défaut, ce crochet ne fait rien, mais nous pouvons y ajouter notre propre fonctionnalité, comme des vérifications qui annulent le transfert s'il y a un problème. +Le contrat ERC-20 d'OpenZeppelin inclut [un hook, `_beforeTokenTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368), qui est appelé avant le transfert d'un jeton. Par défaut, ce hook ne fait rien, mais nous pouvons y ajouter notre propre fonctionnalité, comme des vérifications qui annulent la transaction en cas de problème. -Pour utiliser le crochet, ajoutez cette fonction après le constructeur : +Pour utiliser le hook, ajoutez cette fonction après le constructeur : ```solidity function _beforeTokenTransfer(address from, address to, uint256 amount) @@ -67,42 +69,42 @@ Pour utiliser le crochet, ajoutez cette fonction après le constructeur : } ``` -Certaines parties de cette fonction peuvent vous être nouvelles si vous n'êtes pas très familiarisé avec Solidity : +Certaines parties de cette fonction peuvent vous paraître nouvelles si vous n'êtes pas très familier avec Solidity : ```solidity internal virtual ``` -Le mot-clé `virtual` signifie que tout comme nous avons hérité de la fonctionnalité `d'ERC20` et avons surchargé cette fonction, d'autres contrats peuvent hériter de nous et surcharger aussi cette fonction. +Le mot-clé `virtual` signifie que, tout comme nous avons hérité de la fonctionnalité de `ERC20` et avons substitué cette fonction, d'autres contrats peuvent hériter de nous et substituer également cette fonction. ```solidity override(ERC20) ``` -Nous devons explicitement spécifier que nous [surchargeons](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding) la définition du jeton ERC20 de `_beforeTokenTransfer`. En général, les définitions explicites sont bien meilleures, du point de vue de la sécurité, que les définitions implicites : vous ne pouvez pas oublier que vous avez fait quelque chose si cela se trouve juste devant vous. C'est aussi la raison pour laquelle nous devons spécifier quelle surclasse de `_beforeTokenTransfer` nous surchargeons. +Nous devons spécifier explicitement que nous [substituons](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding) la définition de `_beforeTokenTransfer` du jeton ERC20. En général, les définitions explicites sont bien meilleures du point de vue de la sécurité que les définitions implicites : vous ne pouvez pas oublier que vous avez fait quelque chose si cela se trouve juste devant vous. C'est aussi la raison pour laquelle nous devons spécifier de quelle superclasse nous substituons la fonction `_beforeTokenTransfer`. ```solidity super._beforeTokenTransfer(from, to, amount); ``` -Cette ligne appelle la fonction `_beforeTokenTransfer` du ou des contrats dont nous avons hérité et qui la possèdent. Dans ce cas, il s'agit uniquement `d'ERC20`, `Ownable` n'a pas ce crochet. Bien qu'actuellement `ERC20._beforeTokenTransfer` ne fasse rien, nous l'appelons au cas où une fonctionnalité serait ajoutée à l'avenir (et nous décidons alors de redéployer le contrat, car les contrats ne changent pas après le déploiement). +Cette ligne appelle la fonction `_beforeTokenTransfer` du ou des contrats dont nous avons hérité qui la possèdent. Dans ce cas, il s'agit uniquement de `ERC20`, car `Ownable` n'a pas ce hook. Même si `ERC20._beforeTokenTransfer` ne fait rien actuellement, nous l'appelons au cas où une fonctionnalité serait ajoutée à l'avenir (et que nous décidions alors de redéployer le contrat, car les contrats ne changent pas après leur déploiement). -### Codage des exigences {#coding-the-requirements} +### Coder les exigences {#coding-the-requirements} Nous voulons ajouter ces exigences à la fonction : -- L'adresse `to` ne peut pas être égale à cette `address`, l'adresse du contrat ERC-20 lui-même. +- L'adresse `to` ne peut pas être égale à `address(this)`, l'adresse du contrat ERC-20 lui-même. - L'adresse `to` ne peut pas être vide, elle doit être soit : - - Un compte détenu extérieurement (EOA). Nous ne pouvons pas vérifier directement si une adresse est un EOA, mais nous pouvons vérifier le solde ETH d'une adresse. Les EOA ont presque toujours un solde, même s'ils ne sont plus utilisés - il est difficile de les vider jusqu'au dernier wei. - - Un contrat intelligent. Tester si une adresse est un contrat intelligent est un peu plus difficile. Il existe un opcode qui vérifie la longueur du code externe, appelé [`EXTCODESIZE`](https://www.evm.codes/#3b), mais il n'est pas directement disponible en Solidity. Nous devons utiliser [Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html), qui est l'assembleur EVM, pour cela. Il existe d'autres valeurs que nous pourrions utiliser depuis Solidity ([`
.code` et `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)), mais elles coûtent plus cher. + - Un compte externe (EOA). Nous ne pouvons pas vérifier directement si une adresse est un EOA, mais nous pouvons vérifier le solde en ETH d'une adresse. Les EOA ont presque toujours un solde, même s'ils ne sont plus utilisés. Il est difficile de les vider jusqu'au dernier wei. + - Un contrat intelligent. Vérifier si une adresse est un contrat intelligent est un peu plus difficile. Il existe un opcode qui vérifie la longueur du code externe, appelé [`EXTCODESIZE`](https://www.evm.codes/#3b), mais il n'est pas disponible directement dans Solidity. Pour cela, nous devons utiliser [Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html), qui est l'assembleur de l'EVM. Il existe d'autres valeurs que nous pourrions utiliser à partir de Solidity ([`
.code` et `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)), mais elles coûtent plus cher. -Examinons le nouveau code ligne par ligne : +Passons en revue le nouveau code, ligne par ligne : ```solidity - require(to != address(this), "Can't send tokens to the contract address"); + require(to != address(this), "Impossible d'envoyer des jetons à l'adresse du contrat"); ``` -C'est la première exigence, vérifier que `to` et `cette(address)` ne sont pas la même chose. +C'est la première exigence : vérifier que `to` et `address(this)` ne sont pas la même chose. ```solidity bool isToContract; @@ -111,53 +113,53 @@ C'est la première exigence, vérifier que `to` et `cette(address)` ne sont pas } ``` -C'est ainsi que nous vérifions si une adresse est un contrat. Nous ne pouvons pas recevoir de réponse directement de Yul, nous définissons donc une variable pour contenir le résultat (`isToContract` dans ce cas). La façon dont Yul fonctionne est que chaque opcode est considéré comme une fonction. Nous appelons donc d'abord [`EXTCODESIZE`](https://www.evm.codes/#3b) pour obtenir la taille du contrat, puis utilisons [`GT`](https://www.evm.codes/#11) pour vérifier qu'elle n'est pas nulle (nous travaillons avec des entiers non signés, donc bien sûr elle ne peut pas être négative). Nous écrivons ensuite le résultat dans `isToContract`. +Voici comment nous vérifions si une adresse est un contrat. Nous ne pouvons pas recevoir de sortie directement de Yul. À la place, nous définissons donc une variable pour conserver le résultat (`isToContract` dans ce cas). Yul fonctionne de telle manière que chaque opcode est considéré comme une fonction. Donc, nous appelons d'abord [`EXTCODESIZE`](https://www.evm.codes/#3b) pour obtenir la taille du contrat, puis nous utilisons [`GT`](https://www.evm.codes/#11) pour vérifier qu'elle n'est pas nulle (nous avons affaire à des entiers non signés, donc bien sûr, elle ne peut pas être négative). Nous écrivons ensuite le résultat dans `isToContract`. ```solidity - require(to.balance != 0 || isToContract, "Can't send tokens to an empty address"); + require(to.balance != 0 || isToContract, "Impossible d'envoyer des jetons à une adresse vide"); ``` -Et enfin, nous avons la vérification réelle des adresses vides. +Et enfin, nous avons la vérification effective des adresses vides. ## Accès administratif {#admin-access} -Parfois, il est utile d'avoir un administrateur qui peut annuler des erreurs. Pour réduire le potentiel d'abus, cet administrateur peut être géré par une [multisig](https://blog.logrocket.com/security-choices-multi-signature-wallets/), ce qui signifie que plusieurs personnes doivent accepter une action. Dans cet article, nous aurons deux fonctionnalités administratives : +Il est parfois utile d'avoir un administrateur qui peut annuler des erreurs. Pour réduire le potentiel d'abus, cet administrateur peut être un [multisig](https://blog.logrocket.com/security-choices-multi-signature-wallets/), de sorte que plusieurs personnes doivent approuver une action. Dans cet article, nous verrons deux fonctionnalités administratives : 1. Le gel et le dégel des comptes. Ceci peut être utile, par exemple, lorsqu'un compte pourrait être compromis. 2. Nettoyage des actifs. - Parfois, des fraudeurs envoient des jetons frauduleux au vrai contrat pour gagner en légitimité. Par exemple, [regardez ici](https://optimistic.etherscan.io/token/0x2348b1a1228ddcd2db668c3d30207c3e1852fbbe?a=0x4200000000000000000000000000000000000042). Le contrat ERC-20 légitime est [0x4200....0042](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000042). L'arnaque qui prétend l'être est [0x234....bbe](https://optimistic.etherscan.io/address/0x2348b1a1228ddcd2db668c3d30207c3e1852fbbe). + Parfois, des fraudeurs envoient des jetons frauduleux au contrat du jeton réel pour gagner en légitimité. Par exemple, [voir ici](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe?tab=holders). Le contrat ERC-20 légitime est [0x4200....0042](https://optimism.blockscout.com/token/0x4200000000000000000000000000000000000042). L'arnaque qui prétend être ce contrat est [0x234....bbe](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe). Il est également possible que des personnes envoient par erreur des jetons ERC-20 légitimes à notre contrat, ce qui est une autre raison de vouloir trouver un moyen de les retirer. OpenZeppelin fournit deux mécanismes pour activer l'accès administratif : -- Les contrats [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable) ont un seul propriétaire. Les fonctions ayant le [modificateur](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `onlyOwner` ne peuvent être appelées que par ce propriétaire. Les propriétaires peuvent transférer la propriété à quelqu'un d'autre ou y renoncer complètement. Les droits de tous les autres comptes sont généralement identiques. -- Les contrats [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control) ont [un contrôle d'accès basé sur les rôles (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control). +- Les contrats `Ownable` ont un propriétaire unique. Les fonctions qui ont le [modificateur](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `onlyOwner` ne peuvent être appelées que par ce propriétaire. Les propriétaires peuvent transférer la propriété à quelqu'un d'autre ou y renoncer complètement. Les droits de tous les autres comptes sont généralement identiques. +- Les contrats `AccessControl` ont un [contrôle d'accès basé sur les rôles (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control). -Pour simplifier, dans cet article, nous utilisons `Ownable`. +Par souci de simplicité, nous utilisons `Ownable` dans cet article. -### Geler et dégeler les contrats {#freezing-and-thawing-contracts} +### Gel et dégel des contrats {#freezing-and-thawing-contracts} Le gel et le dégel des contrats nécessitent plusieurs modifications : -- Un [mapping](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) d'adresses à des [booléens](https://en.wikipedia.org/wiki/Boolean_data_type) pour suivre quelles adresses sont gelées. Toutes les valeurs sont initialement à zéro, ce qui, pour les valeurs booléennes, est interprété comme faux. C'est ce que nous voulons, car par défaut, les comptes ne sont pas gelés. +- Un [mapping](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) des adresses vers des [booléens](https://en.wikipedia.org/wiki/Boolean_data_type) pour suivre les adresses qui sont gelées. Toutes les valeurs sont initialement à zéro, ce qui, pour les valeurs booléennes, est interprété comme faux. C'est ce que nous voulons, car par défaut, les comptes ne sont pas gelés. ```solidity mapping(address => bool) public frozenAccounts; ``` -- Des [événements](https://www.tutorialspoint.com/solidity/solidity_events.htm) pour informer quiconque intéressé lorsqu'un compte est gelé ou dégelé. Techniquement, ces événements ne sont pas nécessaires pour ces actions, mais cela aide le code hors chaîne à pouvoir écouter ces événements et savoir ce qui se passe. Il est considéré comme une bonne manière pour un contrat intelligent de les émettre lorsque quelque chose qui pourrait être pertinent pour quelqu'un d'autre se produit. +- Des [événements](https://www.tutorialspoint.com/solidity/solidity_events.htm) pour informer toute personne intéressée lorsqu'un compte est gelé ou dégelé. Techniquement, les événements ne sont pas requis pour ces actions, mais ils aident le code hors chaîne à écouter ces événements et à savoir ce qui se passe. Il est de bon ton pour un contrat intelligent de les émettre lorsque quelque chose qui pourrait intéresser quelqu'un d'autre se produit. - Les événements sont indexés, il sera donc possible de rechercher toutes les fois qu'un compte a été gelé ou dégelé. + Les événements sont indexés, il sera donc possible de rechercher toutes les fois où un compte a été gelé ou dégelé. ```solidity - // When accounts are frozen or unfrozen + // Lorsque les comptes sont gelés ou dégelés event AccountFrozen(address indexed _addr); event AccountThawed(address indexed _addr); ``` -- Fonctions pour geler et dégeler les comptes. Ces deux fonctions sont presque identiques, nous n'expliquerons donc que la fonction de gel. +- Fonctions pour geler et dégeler les comptes. Ces deux fonctions sont presque identiques, nous n'examinerons donc que la fonction de gel. ```solidity function freezeAccount(address addr) @@ -165,27 +167,27 @@ Le gel et le dégel des contrats nécessitent plusieurs modifications : onlyOwner ``` - Les fonctions marquées comme [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm) peuvent être appelées à partir d'autres contrats intelligents ou directement par une transaction. + Les fonctions marquées comme [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm) peuvent être appelées depuis d'autres contrats intelligents ou directement par une transaction. ```solidity { - require(!frozenAccounts[addr], "Account already frozen"); + require(!frozenAccounts[addr], "Compte déjà gelé"); frozenAccounts[addr] = true; emit AccountFrozen(addr); } // freezeAccount ``` - Si le compte est déjà gelé, annulez. Sinon, gelez-le et `émettez` un événement. + Si le compte est déjà gelé, la transaction est annulée. Sinon, gelez-le et `émettez` un événement. -- Modifiez `_beforeTokenTransfer` pour empêcher le transfert d'argent depuis un compte gelé. Notez que l'argent peut toujours être transféré vers le compte gelé. +- Modifiez `_beforeTokenTransfer` pour empêcher que des fonds ne soient déplacés depuis un compte gelé. Notez que des fonds peuvent toujours être transférés vers le compte gelé. ```solidity - require(!frozenAccounts[from], "The account is frozen"); + require(!frozenAccounts[from], "Le compte est gelé"); ``` ### Nettoyage des actifs {#asset-cleanup} -Pour libérer les jetons ERC-20 détenus par ce contrat, nous devons appeler une fonction sur le contrat token auquel ils appartiennent, soit [`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer) soit [`approve`](https://eips.ethereum.org/EIPS/eip-20#approve). Cela ne sert à rien de gaspiller du gaz dans ce cas en quotas, autant transférer directement. +Pour libérer les jetons ERC-20 détenus par ce contrat, nous devons appeler une fonction sur le contrat de jeton auquel ils appartiennent, soit [`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer) soit [`approve`](https://eips.ethereum.org/EIPS/eip-20#approve). Il est inutile de gaspiller du gaz sur des allocations dans ce cas, autant transférer directement. ```solidity function cleanupERC20( @@ -198,7 +200,7 @@ Pour libérer les jetons ERC-20 détenus par ce contrat, nous devons appeler une IERC20 token = IERC20(erc20); ``` -C'est la syntaxe pour créer un objet pour un contrat lorsque nous recevons l'adresse. Nous pouvons faire cela parce que nous avons la définition des jetons ERC20 dans le code source (voir ligne 4), et ce fichier inclut [la définition pour IERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol), l'interface pour un contrat ERC-20 d'OpenZeppelin. +C'est la syntaxe pour créer un objet pour un contrat lorsque nous recevons l'adresse. Nous pouvons le faire parce que nous avons la définition des jetons ERC20 dans le code source (voir la ligne 4), et ce fichier inclut [la définition de IERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol), l'interface pour un contrat ERC-20 OpenZeppelin. ```solidity uint balance = token.balanceOf(address(this)); @@ -206,8 +208,10 @@ C'est la syntaxe pour créer un objet pour un contrat lorsque nous recevons l'ad } ``` -C'est une fonction de nettoyage, nous ne voulons donc probablement laisser aucun jeton. Au lieu d'obtenir le solde de l'utilisateur manuellement, autant automatiser le processus. +Il s'agit d'une fonction de nettoyage, donc nous ne voulons vraisemblablement laisser aucun jeton. Au lieu de demander manuellement le solde à l'utilisateur, nous pouvons tout aussi bien automatiser le processus. ## Conclusion {#conclusion} -Ce n'est pas une solution parfaite - il n'existe pas de solution parfaite au problème « l'utilisateur a fait une erreur ». Cependant, utiliser ce type de vérifications peut au moins prévenir certaines erreurs. La capacité à geler des comptes, bien que dangereuse, peut être utilisée pour limiter les dégâts de certaines attaques en refusant au pirate les fonds volés. +Ce n'est pas une solution parfaite. Il n'y a pas de solution parfaite au problème « l'utilisateur a fait une erreur ». Cependant, l'utilisation de ce type de vérifications peut au moins éviter certaines erreurs. La possibilité de geler des comptes, bien que dangereuse, peut être utilisée pour limiter les dégâts de certains piratages en refusant au pirate les fonds volés. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/ethereum-for-web2-auth/index.md b/public/content/translations/fr/developers/tutorials/ethereum-for-web2-auth/index.md new file mode 100644 index 00000000000..0eb12e3b3e8 --- /dev/null +++ b/public/content/translations/fr/developers/tutorials/ethereum-for-web2-auth/index.md @@ -0,0 +1,886 @@ +--- +title: Utiliser Ethereum pour l'authentification web2 +description: "Après avoir lu ce tutoriel, un développeur sera en mesure d'intégrer la connexion Ethereum (web3) à la connexion SAML, une norme utilisée dans le web2 pour fournir une authentification unique et d'autres services connexes. Cela permet d'authentifier l'accès aux ressources web2 par le biais de signatures Ethereum, les attributs de l'utilisateur provenant d'attestations." +author: Ori Pomerantz +tags: [ "web2", "authentification", "eas" ] +skill: beginner +lang: fr +published: 2025-04-30 +--- + +## Introduction + +[SAML](https://www.onelogin.com/learn/saml) est une norme utilisée sur le web2 pour permettre à un [fournisseur d'identité (IdP)](https://en.wikipedia.org/wiki/Identity_provider#SAML_identity_provider) de fournir des informations sur l'utilisateur aux [fournisseurs de services (SP)](https://en.wikipedia.org/wiki/Service_provider_\(SAML\)). + +Dans ce tutoriel, vous apprendrez comment intégrer les signatures Ethereum avec SAML pour permettre aux utilisateurs d'utiliser leurs portefeuilles Ethereum pour s'authentifier auprès de services web2 qui ne prennent pas encore en charge Ethereum de manière native. + +Notez que ce tutoriel s'adresse à deux publics distincts : + +- Les personnes familières avec Ethereum qui comprennent Ethereum et qui ont besoin d'apprendre SAML +- Les personnes familières avec le Web2 qui comprennent SAML et l'authentification web2 et qui ont besoin d'apprendre Ethereum + +Par conséquent, il contiendra beaucoup de matériel d'introduction que vous connaissez déjà. N'hésitez pas à le sauter. + +### SAML pour les personnes familières avec Ethereum + +SAML est un protocole centralisé. Un fournisseur de services (SP) n'accepte les assertions (telles que « voici mon utilisateur John, il devrait avoir les permissions pour faire A, B et C ») d'un fournisseur d'identité (IdP) que s'il a une relation de confiance préexistante soit avec lui, soit avec [l'autorité de certification](https://www.ssl.com/article/what-is-a-certificate-authority-ca/) qui a signé le certificat de cet IdP. + +Par exemple, le SP peut être une agence de voyage fournissant des services de voyage aux entreprises, et l'IdP peut être le site web interne d'une entreprise. Lorsque des employés doivent réserver un voyage d'affaires, l'agence de voyage les envoie pour une authentification par l'entreprise avant de les laisser réserver le voyage. + +![Processus SAML étape par étape](./fig-01-saml.png) + +C'est ainsi que les trois entités, le navigateur, le SP et l'IdP, négocient l'accès. Le SP n'a pas besoin de connaître à l'avance quoi que ce soit sur l'utilisateur qui utilise le navigateur, il lui suffit de faire confiance à l'IdP. + +### Ethereum pour les personnes familières avec SAML + +Ethereum est un système décentralisé. + +![Connexion Ethereum](./fig-02-eth-logon.png) + +Les utilisateurs ont une clé privée (généralement conservée dans une extension de navigateur). À partir de la clé privée, vous pouvez dériver une clé publique, et à partir de celle-ci, une adresse de 20 octets. Lorsque les utilisateurs doivent se connecter à un système, il leur est demandé de signer un message avec un nonce (une valeur à usage unique). Le serveur peut vérifier que la signature a été créée par cette adresse. + +![Obtenir des données supplémentaires à partir des attestations](./fig-03-eas-data.png) + +La signature ne vérifie que l'adresse Ethereum. Pour obtenir d'autres attributs utilisateur, vous utilisez généralement des [attestations](https://attest.org/). Une attestation comporte généralement les champs suivants : + +- **Attestateur**, l'adresse qui a fait l'attestation +- **Destinataire**, l'adresse à laquelle s'applique l'attestation +- **Données**, les données attestées, telles que le nom, les permissions, etc. +- **Schéma**, l'ID du schéma utilisé pour interpréter les données. + +En raison de la nature décentralisée d'Ethereum, tout utilisateur peut faire des attestations. L'identité de l'attestateur est importante pour identifier les attestations que nous considérons comme fiables. + +## Configuration + +La première étape consiste à avoir un SP SAML et un IdP SAML qui communiquent entre eux. + +1. Téléchargez le logiciel. L'exemple de logiciel pour cet article se trouve [sur github](https://github.com/qbzzt/250420-saml-ethereum). Les différentes étapes sont stockées dans différentes branches, pour cette étape, vous voulez `saml-only` + + ```sh + git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only + cd 250420-saml-ethereum + pnpm install + ``` + +2. Créez des clés avec des certificats auto-signés. Cela signifie que la clé est sa propre autorité de certification et doit être importée manuellement chez le fournisseur de services. Consultez [la documentation OpenSSL](https://docs.openssl.org/master/man1/openssl-req/) pour plus d'informations. + + ```sh + mkdir keys + cd keys + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-sp.crt -keyout saml-sp.pem -subj /CN=sp/ + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-idp.crt -keyout saml-idp.pem -subj /CN=idp/ + cd .. + ``` + +3. Démarrez les serveurs (à la fois SP et IdP) + + ```sh + pnpm start + ``` + +4. Naviguez vers le SP à l'URL [http://localhost:3000/](http://localhost:3000/) et cliquez sur le bouton pour être redirigé vers l'IdP (port 3001). + +5. Fournissez à l'IdP votre adresse e-mail et cliquez sur **Se connecter au fournisseur de services**. Constatez que vous êtes redirigé vers le fournisseur de services (port 3000) et qu'il vous reconnaît par votre adresse e-mail. + +### Explication détaillée + +Voici ce qui se passe, étape par étape : + +![Connexion SAML normale sans Ethereum](./fig-04-saml-no-eth.png) + +#### src/config.mts + +Ce fichier contient la configuration du fournisseur d'identité et du fournisseur de services. Normalement, il s'agirait de deux entités différentes, mais ici, nous pouvons partager le code par souci de simplicité. + +```typescript +const fs = await import("fs") + +const protocol="http" +``` + +Pour l'instant, nous ne faisons que des tests, il est donc acceptable d'utiliser HTTP. + +```typescript +export const spCert = fs.readFileSync("keys/saml-sp.crt").toString() +export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString() +``` + +Lisez les clés publiques, qui sont normalement disponibles pour les deux composants (et soit directement approuvées, soit signées par une autorité de certification de confiance). + +```typescript +export const spPort = 3000 +export const spHostname = "localhost" +export const spDir = "sp" + +export const idpPort = 3001 +export const idpHostname = "localhost" +export const idpDir = "idp" + +export const spUrl = `${protocol}://${spHostname}:${spPort}/${spDir}` +export const idpUrl = `${protocol}://${idpHostname}:${idpPort}/${idpDir}` +``` + +Les URL des deux composants. + +```typescript +export const spPublicData = { +``` + +Les données publiques pour le fournisseur de services. + +```typescript + entityID: `${spUrl}/metadata`, +``` + +Par convention, en SAML, l'`entityID` est l'URL où les métadonnées de l'entité sont disponibles. Ces métadonnées correspondent aux données publiques ici, sauf qu'elles sont sous forme XML. + +```typescript + wantAssertionsSigned: true, + authnRequestsSigned: false, + signingCert: spCert, + allowCreate: true, + assertionConsumerService: [{ + Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + Location: `${spUrl}/assertion`, + }] + } +``` + +La définition la plus importante pour nos besoins est `assertionConsumerServer`. Cela signifie que pour affirmer quelque chose (par exemple, « l'utilisateur qui vous envoie cette information est quelqu'un@exemple.com ») au fournisseur de services, nous devons utiliser [HTTP POST](https://www.w3schools.com/tags/ref_httpmethods.asp) à l'URL `http://localhost:3000/sp/assertion`. + +```typescript +export const idpPublicData = { + entityID: `${idpUrl}/metadata`, + signingCert: idpCert, + wantAuthnRequestsSigned: false, + singleSignOnService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/login` + }], + singleLogoutService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/logout` + }], + } +``` + +Les données publiques pour le fournisseur d'identité sont similaires. Il spécifie que pour connecter un utilisateur, vous devez faire une requête POST sur `http://localhost:3001/idp/login` et pour déconnecter un utilisateur, vous devez faire une requête POST sur `http://localhost:3001/idp/logout`. + +#### src/sp.mts + +Ceci est le code qui implémente un fournisseur de services. + +```typescript +import * as config from "./config.mts" +const fs = await import("fs") +const saml = await import("samlify") +``` + +Nous utilisons la bibliothèque [`samlify`](https://www.npmjs.com/package/samlify) pour implémenter SAML. + +```typescript +import * as validator from "@authenio/samlify-node-xmllint" +saml.setSchemaValidator(validator) +``` + +La bibliothèque `samlify` s'attend à ce qu'un paquet valide que le XML est correct, signé avec la clé publique attendue, etc. Nous utilisons [`@authenio/samlify-node-xmllint`](https://www.npmjs.com/package/@authenio/samlify-node-xmllint) à cette fin. + +```typescript +const express = (await import("express")).default +const spRouter = express.Router() +const app = express() +``` + +Un [`Router`](https://expressjs.com/en/5x/api.html#router) [`express`](https://expressjs.com/) est un « mini site web » qui peut être monté à l'intérieur d'un site web. Dans ce cas, nous l'utilisons pour regrouper toutes les définitions du fournisseur de services. + +```typescript +const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString() + +const sp = saml.ServiceProvider({ + privateKey: spPrivateKey, + ...config.spPublicData +}) +``` + +La propre représentation du fournisseur de services est l'ensemble des données publiques et la clé privée qu'il utilise pour signer les informations. + +```typescript +const idp = saml.IdentityProvider(config.idpPublicData); +``` + +Les données publiques contiennent tout ce que le fournisseur de services doit savoir sur le fournisseur d'identité. + +```typescript +spRouter.get(`/metadata`, + (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata()) +) +``` + +Pour permettre l'interopérabilité avec d'autres composants SAML, les fournisseurs de services et d'identité doivent rendre leurs données publiques (appelées métadonnées) disponibles au format XML dans `/metadata`. + +```typescript +spRouter.post(`/assertion`, +``` + +C'est la page à laquelle le navigateur accède pour s'identifier. L'assertion inclut l'identifiant de l'utilisateur (ici, nous utilisons l'adresse e-mail) et peut inclure des attributs supplémentaires. C'est le gestionnaire de l'étape 7 du diagramme de séquence ci-dessus. + +```typescript + async (req, res) => { + // console.log(`Réponse SAML :\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}`) +``` + +Vous pouvez utiliser la commande commentée pour voir les données XML fournies dans l'assertion. Il est [encodé en base64](https://en.wikipedia.org/wiki/Base64). + +```typescript + try { + const loginResponse = await sp.parseLoginResponse(idp, 'post', req); +``` + +Analysez la demande de connexion provenant du serveur d'identité. + +```typescript + res.send(` + + +

Bonjour ${loginResponse.extract.nameID}

+ + + `) + res.send(); +``` + +Envoyez une réponse HTML, juste pour montrer à l'utilisateur que nous avons bien reçu la connexion. + +```typescript + } catch (err) { + console.error('Erreur lors du traitement de la réponse SAML :', err); + res.status(400).send('L\'authentification SAML a échoué'); + } + } +) +``` + +Informez l'utilisateur en cas d'échec. + +```typescript +spRouter.get('/login', +``` + +Créez une demande de connexion lorsque le navigateur tente d'accéder à cette page. C'est le gestionnaire de l'étape 1 du diagramme de séquence ci-dessus. + +```typescript + async (req, res) => { + const loginRequest = await sp.createLoginRequest(idp, "post") +``` + +Obtenez les informations pour poster une demande de connexion. + +```typescript + res.send(` + + + +``` + +Cette page soumet le formulaire (voir ci-dessous) automatiquement. De cette façon, l'utilisateur n'a rien à faire pour être redirigé. C'est l'étape 2 du diagramme de séquence ci-dessus. + +```typescript +
+``` + +Faites une requête POST à `loginRequest.entityEndpoint` (l'URL du point de terminaison du fournisseur d'identité). + +```typescript + +``` + +Le nom de l'entrée est `loginRequest.type` (`SAMLRequest`). Le contenu de ce champ est `loginRequest.context`, qui est à nouveau du XML encodé en base64. + +```typescript +
+ + + `) + } +) + +app.use(express.urlencoded({extended: true})) +``` + +[Ce middleware](https://expressjs.com/en/5x/api.html#express.urlencoded) lit le corps de la [requête HTTP](https://www.tutorialspoint.com/http/http_requests.htm). Par défaut, express l'ignore, car la plupart des requêtes ne le nécessitent pas. Nous en avons besoin car POST utilise le corps de la requête. + +```typescript +app.use(`/${config.spDir}`, spRouter) +``` + +Montez le routeur dans le répertoire du fournisseur de services (`/sp`). + +```typescript +app.get("/", (req, res) => { + res.send(` + + + + + + `) +}) +``` + +Si un navigateur tente d'accéder au répertoire racine, fournissez-lui un lien vers la page de connexion. + +```typescript +app.listen(config.spPort, () => { + console.log(`le fournisseur de services fonctionne sur http://${config.spHostname}:${config.spPort}`) +}) +``` + +Écoutez le `spPort` avec cette application express. + +#### src/idp.mts + +C'est le fournisseur d'identité. Il est très similaire au fournisseur de services, les explications ci-dessous concernent les parties qui sont différentes. + +```typescript +const xmlParser = new (await import("fast-xml-parser")).XMLParser( + { + ignoreAttributes: false, // Préserver les attributs + attributeNamePrefix: "@_", // Préfixe pour les attributs + } +) +``` + +Nous devons lire et comprendre la requête XML que nous recevons du fournisseur de services. + +```typescript +const getLoginPage = requestId => ` +``` + +Cette fonction crée la page avec le formulaire auto-soumis qui est retourné à l'étape 4 du diagramme de séquence ci-dessus. + +```typescript + + + Page de connexion + + +

Page de connexion

+
+ + Adresse e-mail : +
+ +``` + +Nous envoyons deux champs au fournisseur de services : + +1. Le `requestId` auquel nous répondons. +2. L'identifiant de l'utilisateur (nous utilisons pour l'instant l'adresse e-mail fournie par l'utilisateur). + +```typescript +
+ + + +const idpRouter = express.Router() + +idpRouter.post("/loginSubmitted", async (req, res) => { + const loginResponse = await idp.createLoginResponse( +``` + +C'est le gestionnaire de l'étape 5 du diagramme de séquence ci-dessus. [`idp.createLoginResponse`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L73-L125) crée la réponse de connexion. + +```typescript + sp, + { + authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + audience: sp.entityID, +``` + +L'audience est le fournisseur de services. + +```typescript + extract: { + request: { + id: req.body.requestId + } + }, +``` + +Informations extraites de la requête. Le seul paramètre qui nous intéresse dans la requête est le requestId, qui permet au fournisseur de services de faire correspondre les requêtes et leurs réponses. + +```typescript + signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Assurer la signature +``` + +Nous avons besoin de `signingKey` pour avoir les données nécessaires à la signature de la réponse. Le fournisseur de services ne fait pas confiance aux requêtes non signées. + +```typescript + }, + "post", + { + email: req.body.email +``` + +C'est le champ contenant les informations de l'utilisateur que nous renvoyons au fournisseur de services. + +```typescript + } + ); + + res.send(` + + + + +
+ +
+ + + `) +}) +``` + +Encore une fois, utilisez un formulaire auto-soumis. C'est l'étape 6 du diagramme de séquence ci-dessus. + +```typescript + +// Point de terminaison IdP pour les requêtes de connexion +idpRouter.post(`/login`, +``` + +C'est le point de terminaison qui reçoit une demande de connexion du fournisseur de services. C'est le gestionnaire de l'étape 3 du diagramme de séquence ci-dessus. + +```typescript + async (req, res) => { + try { + // Solution de contournement car je n'ai pas réussi à faire fonctionner parseLoginRequest. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getLoginPage(samlRequest["samlp:AuthnRequest"]["@_ID"])) +``` + +Nous devrions pouvoir utiliser [`idp.parseLoginRequest`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L127-L144) pour lire l'ID de la demande d'authentification. Cependant, je n'ai pas réussi à le faire fonctionner et cela ne valait pas la peine de passer beaucoup de temps dessus, alors j'utilise simplement un [analyseur XML générique](https://www.npmjs.com/package/fast-xml-parser). L'information dont nous avons besoin est l'attribut `ID` à l'intérieur de la balise ``, qui se trouve au niveau supérieur du XML. + +## Utilisation des signatures Ethereum + +Maintenant que nous pouvons envoyer une identité d'utilisateur au fournisseur de services, la prochaine étape est d'obtenir l'identité de l'utilisateur de manière fiable. Viem nous permet de simplement demander au portefeuille l'adresse de l'utilisateur, mais cela signifie demander l'information au navigateur. Nous ne contrôlons pas le navigateur, nous ne pouvons donc pas faire confiance automatiquement à la réponse que nous en recevons. + +Au lieu de cela, l'IdP va envoyer au navigateur une chaîne de caractères à signer. Si le portefeuille dans le navigateur signe cette chaîne, cela signifie qu'il s'agit bien de cette adresse (c'est-à-dire qu'il connaît la clé privée qui correspond à l'adresse). + +Pour voir cela en action, arrêtez l'IdP et le SP existants et exécutez ces commandes : + +```sh +git checkout eth-signatures +pnpm install +pnpm start +``` + +Ensuite, naviguez vers [le SP](http://localhost:3000) et suivez les instructions. + +Notez qu'à ce stade, nous ne savons pas comment obtenir l'adresse e-mail à partir de l'adresse Ethereum, donc à la place nous signalons `@bad.email.address` au SP. + +### Explication détaillée + +Les changements se situent aux étapes 4-5 du diagramme précédent. + +![SAML avec une signature Ethereum](./fig-05-saml-w-signature.png) + +Le seul fichier que nous avons modifié est `idp.mts`. Voici les parties modifiées. + +```typescript +import { v4 as uuidv4 } from 'uuid' +import { verifyMessage } from 'viem' +``` + +Nous avons besoin de ces deux bibliothèques supplémentaires. Nous utilisons [`uuid`](https://www.npmjs.com/package/uuid) pour créer la valeur [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). La valeur elle-même n'a pas d'importance, seulement le fait qu'elle n'est utilisée qu'une seule fois. + +La bibliothèque [`viem`](https://viem.sh/) nous permet d'utiliser les définitions d'Ethereum. Ici, nous en avons besoin pour vérifier que la signature est bien valide. + +```typescript +const loginPrompt = "Pour accéder au fournisseur de services, signez ce nonce : " +``` + +Le portefeuille demande à l'utilisateur la permission de signer le message. Un message qui n'est qu'un nonce pourrait dérouter les utilisateurs, c'est pourquoi nous incluons cette invite. + +```typescript +// Conserver les requestIDs ici +let nonces = {} +``` + +Nous avons besoin des informations de la requête pour pouvoir y répondre. Nous pourrions l'envoyer avec la requête (étape 4) et la recevoir en retour (étape 5). Cependant, nous ne pouvons pas faire confiance aux informations que nous recevons du navigateur, qui est sous le contrôle d'un utilisateur potentiellement hostile. Il est donc préférable de le stocker ici, avec le nonce comme clé. + +Notez que nous le faisons ici en tant que variable par souci de simplicité. Cependant, cela présente plusieurs inconvénients : + +- Nous sommes vulnérables à une attaque par déni de service. Un utilisateur malveillant pourrait tenter de se connecter plusieurs fois, remplissant ainsi notre mémoire. +- Si le processus IdP doit être redémarré, nous perdons les valeurs existantes. +- Nous ne pouvons pas répartir la charge sur plusieurs processus, car chacun aurait sa propre variable. + +Sur un système de production, nous utiliserions une base de données et mettrions en œuvre un mécanisme d'expiration. + +```typescript +const getSignaturePage = requestId => { + const nonce = uuidv4() + nonces[nonce] = requestId +``` + +Créez un nonce et stockez le `requestId` pour une utilisation future. + +```typescript + return ` + + + + + +

Veuillez signer

+ +
+ + + +` +} +``` + +Le reste n'est que du HTML standard. + +```typescript +idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => { +``` + +C'est le gestionnaire de l'étape 5 du diagramme de séquence. + +```typescript + const requestId = nonces[req.params.nonce] + if (requestId === undefined) { + res.send("Mauvais nonce") + return ; + } + + nonces[req.params.nonce] = undefined +``` + +Obtenez l'ID de la requête et supprimez le nonce de `nonces` pour vous assurer qu'il ne peut pas être réutilisé. + +```typescript + try { +``` + +Parce qu'il y a tant de façons pour que la signature soit invalide, nous l'enveloppons dans un `try ...` bloc `catch` pour intercepter toute erreur levée. + +```typescript + const validSignature = await verifyMessage({ + address: req.params.account, + message: `${loginPrompt}${req.params.nonce}`, + signature: req.params.signature + }) +``` + +Utilisez [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) pour implémenter l'étape 5.5 du diagramme de séquence. + +```typescript + if (!validSignature) + throw("Mauvaise signature") + } catch (err) { + res.send("Erreur :" + err) + return ; + } +``` + +Le reste du gestionnaire est équivalent à ce que nous avons fait dans le gestionnaire `/loginSubmitted` précédemment, à l'exception d'un petit changement. + +```typescript + const loginResponse = await idp.createLoginResponse( + . + . + . + { + email: req.params.account + "@bad.email.address" + } + ); +``` + +Nous n'avons pas l'adresse e-mail réelle (nous l'obtiendrons dans la section suivante), donc pour l'instant nous retournons l'adresse Ethereum et la marquons clairement comme n'étant pas une adresse e-mail. + +```typescript +// Point de terminaison IdP pour les requêtes de connexion +idpRouter.post(`/login`, + async (req, res) => { + try { + // Solution de contournement car je n'ai pas réussi à faire fonctionner parseLoginRequest. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getSignaturePage(samlRequest["samlp:AuthnRequest"]["@_ID"])) + } catch (err) { + console.error('Erreur lors du traitement de la réponse SAML :', err); + res.status(400).send('L\'authentification SAML a échoué'); + } + } +) +``` + +Au lieu de `getLoginPage`, utilisez maintenant `getSignaturePage` dans le gestionnaire de l'étape 3. + +## Obtenir l'adresse e-mail + +La prochaine étape consiste à obtenir l'adresse e-mail, l'identifiant demandé par le fournisseur de services. Pour ce faire, nous utilisons le [service d'attestation Ethereum (EAS)](https://attest.org/). + +Le moyen le plus simple d'obtenir des attestations est d'utiliser [l'API GraphQL](https://docs.attest.org/docs/developer-tools/api). Nous utilisons cette requête : + +``` +query GetAttestationsByRecipient { + attestations( + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } + take: 1 + ) { + data + id + attester + } +} +``` + +Ce [`schemaId`](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977) ne comprend qu'une adresse e-mail. Cette requête demande des attestations de ce schéma. Le sujet de l'attestation est appelé le `destinataire`. Il s'agit toujours d'une adresse Ethereum. + +Attention : la manière dont nous obtenons les attestations ici présente deux problèmes de sécurité. + +- Nous nous rendons au point de terminaison de l'API, `https://optimism.easscan.org/graphql`, qui est un composant centralisé. Nous pouvons obtenir l'attribut `id` et ensuite faire une recherche sur la chaîne pour vérifier qu'une attestation est réelle, mais le point de terminaison de l'API peut toujours censurer les attestations en ne nous informant pas à leur sujet. + + Ce problème n'est pas impossible à résoudre, nous pourrions exécuter notre propre point de terminaison GraphQL et obtenir les attestations à partir des journaux de la chaîne, mais c'est excessif pour nos besoins. + +- Nous ne regardons pas l'identité de l'attestateur. N'importe qui peut nous fournir de fausses informations. Dans une implémentation réelle, nous aurions un ensemble d'attestateurs de confiance et ne regarderions que leurs attestations. + +Pour voir cela en action, arrêtez l'IdP et le SP existants et exécutez ces commandes : + +```sh +git checkout email-address +pnpm install +pnpm start +``` + +Fournissez ensuite votre adresse e-mail. Vous avez deux façons de le faire : + +- Importez un portefeuille à l'aide d'une clé privée et utilisez la clé privée de test `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`. + +- Ajoutez une attestation pour votre propre adresse e-mail : + + 1. Naviguez vers [le schéma dans l'explorateur d'attestation](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977). + + 2. Cliquez sur **Attester avec le schéma**. + + 3. Entrez votre adresse Ethereum comme destinataire, votre adresse e-mail comme adresse e-mail, et sélectionnez **Sur la chaîne**. Cliquez ensuite sur **Faire une attestation**. + + 4. Approuvez la transaction dans votre portefeuille. Vous aurez besoin de quelques ETH sur [la blockchain Optimism](https://app.optimism.io/bridge/deposit) pour payer le gaz. + +Dans tous les cas, après avoir fait cela, naviguez vers [http://localhost:3000](http://localhost:3000) et suivez les instructions. Si vous avez importé la clé privée de test, l'e-mail que vous recevez est `test_addr_0@example.com`. Si vous avez utilisé votre propre adresse, ce devrait être celle que vous avez attestée. + +### Explication détaillée + +![Passer de l'adresse Ethereum à l'e-mail](./fig-06-saml-sig-n-email.png) + +Les nouvelles étapes sont la communication GraphQL, étapes 5.6 et 5.7. + +Encore une fois, voici les parties modifiées de `idp.mts`. + +```typescript +import { GraphQLClient } from 'graphql-request' +import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk' +``` + +Importez les bibliothèques dont nous avons besoin. + +```typescript +const graphqlEndpointUrl = "https://optimism.easscan.org/graphql" +``` + +Il existe [un point de terminaison distinct pour chaque blockchain](https://docs.attest.org/docs/developer-tools/api). + +```typescript +const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch }) +``` + +Créez un nouveau client `GraphQLClient` que nous pouvons utiliser pour interroger le point de terminaison. + +```typescript +const graphqlSchema = 'string emailAddress' +const graphqlEncoder = new SchemaEncoder(graphqlSchema) +``` + +GraphQL ne nous donne qu'un objet de données opaque avec des octets. Pour le comprendre, nous avons besoin du schéma. + +```typescript +const ethereumAddressToEmail = async ethAddr => { +``` + +Une fonction pour passer d'une adresse Ethereum à une adresse e-mail. + +```typescript + const query = ` + query GetAttestationsByRecipient { +``` + +C'est une requête GraphQL. + +```typescript + attestations( +``` + +Nous recherchons des attestations. + +```typescript + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } +``` + +Les attestations que nous voulons sont celles de notre schéma, où le destinataire est `getAddress(ethAddr)`. La fonction [`getAddress`](https://viem.sh/docs/utilities/getAddress#getaddress) s'assure que notre adresse a la bonne [somme de contrôle](https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md). C'est nécessaire car GraphQL est sensible à la casse. "0xBAD060A7", "0xBad060A7", et "0xbad060a7" sont des valeurs différentes. + +```typescript + take: 1 +``` + +Quel que soit le nombre d'attestations que nous trouvons, nous ne voulons que la première. + +```typescript + ) { + data + id + attester + } + }` +``` + +Les champs que nous voulons recevoir. + +- `attester` : l'adresse qui a soumis l'attestation. Normalement, cela est utilisé pour décider de faire confiance ou non à l'attestation. +- `id` : l'ID de l'attestation. Vous pouvez utiliser cette valeur pour [lire l'attestation sur la chaîne](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000021?tab=read_proxy&source_address=0x4E0275Ea5a89e7a3c1B58411379D1a0eDdc5b088#0xa3112a64) afin de vérifier que les informations de la requête GraphQL sont correctes. +- `data` : les données du schéma (dans ce cas, l'adresse e-mail). + +```typescript + const queryResult = await graphqlClient.request(query) + + if (queryResult.attestations.length == 0) + return "no_address@available.is" +``` + +S'il n'y a pas d'attestation, retournez une valeur qui est manifestement incorrecte, mais qui semblerait valide pour le fournisseur de services. + +```typescript + const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data) + return attestationDataFields[0].value.value +} +``` + +S'il y a une valeur, utilisez `decodeData` pour décoder les données. Nous n'avons pas besoin des métadonnées qu'il fournit, juste de la valeur elle-même. + +```typescript + const loginResponse = await idp.createLoginResponse( + sp, + { + . + . + . + }, + "post", + { + email: await ethereumAddressToEmail(req.params.account) + } + ); +``` + +Utilisez la nouvelle fonction pour obtenir l'adresse e-mail. + +## Qu'en est-il de la décentralisation ? + +Dans cette configuration, les utilisateurs ne peuvent pas prétendre être quelqu'un qu'ils ne sont pas, tant que nous nous appuyons sur des attestateurs dignes de confiance pour la correspondance entre l'adresse Ethereum et l'adresse e-mail. Cependant, notre fournisseur d'identité est toujours un composant centralisé. Quiconque possède la clé privée du fournisseur d'identité peut envoyer de fausses informations au fournisseur de services. + +Il pourrait y avoir une solution utilisant le [calcul multipartite sécurisé (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation). J'espère en parler dans un futur tutoriel. + +## Conclusion + +L'adoption d'une norme de connexion, comme les signatures Ethereum, est confrontée au problème de l'œuf et de la poule. Les fournisseurs de services veulent s'adresser au marché le plus large possible. Les utilisateurs veulent pouvoir accéder aux services sans avoir à se soucier de la prise en charge de leur norme de connexion. +La création d'adaptateurs, tels qu'un IdP Ethereum, peut nous aider à surmonter cet obstacle. + +[Voir ici pour plus de mon travail](https://cryptodocguy.pro/). diff --git a/public/content/translations/fr/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md b/public/content/translations/fr/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md index d2b4a64b74c..6eb11eeb0c2 100644 --- a/public/content/translations/fr/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md +++ b/public/content/translations/fr/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md @@ -1,60 +1,62 @@ --- -title: Bien débuter avec le développement Ethereum -description: "Ceci est un guide permettant aux débutants de s'initier avec le développement Ethereum. Nous allons vous guider de la création d'un point d'accès à l'API à l'écriture de votre premier script Web3, en passant par celle d'une requête en ligne de commande ! Aucune expérience préalable dans le développement de blockchain n'est requise !" +title: "Bien débuter avec le développement Ethereum" +description: "Ceci est un guide pour débutants pour se lancer dans le développement sur Ethereum. Nous allons vous guider de la création d'un point d'accès à l'API à l'écriture de votre premier script Web3, en passant par celle d'une requête en ligne de commande ! Aucune expérience préalable dans le développement de blockchain n'est requise !" author: "Elan Halpern" tags: - - "javascript" - - "ethers.js" - - "nœuds" - - "requêtes" - - "alchemy" + [ + "javascript", + "ethers.js", + "nœuds", + "requêtes", + "alchemy" + ] skill: beginner lang: fr published: 2020-10-30 -source: Moyen +source: Medium sourceUrl: https://medium.com/alchemy-api/getting-started-with-ethereum-development-using-alchemy-c3d6a45c567f --- -![Logos Ethereum et Alchemy](./ethereum-alchemy.png) +![Logos d'Ethereum et d'Alchemy](./ethereum-alchemy.png) -Ceci est un guide pour les débutants qui souhaitent se lancer dans le développement Ethereum. Pour ce tutoriel, nous allons utiliser [Alchemy](https://alchemyapi.io/), la principale plateforme de développement blockchain qui sert des millions d'utilisateurs parmi 70 % des principales applications blockchain, dont Maker, 0x, MyEtherWallet, Dharma et Kyber. Alchemy va nous permettre d'accéder à un point de terminaison API sur la chaîne Ethereum afin que nous puissions lire et écrire des transactions. +Ceci est un guide pour débutants pour se lancer dans le développement sur Ethereum. Pour ce tutoriel, nous utiliserons [Alchemy](https://alchemyapi.io/), la principale plateforme de développement blockchain qui alimente des millions d'utilisateurs issus de 70 % des meilleures applications blockchain, notamment Maker, 0x, MyEtherWallet, Dharma et Kyber. Alchemy nous donnera accès à un point de terminaison d'API sur la chaîne Ethereum afin que nous puissions lire et écrire des transactions. -Nous vous guiderons à travers toutes les étapes, de votre inscription sur Alchemy à votre premier script Web3 ! Aucune expérience préalable dans le développement de blockchain n'est requise ! +Nous vous guiderons de l'inscription sur Alchemy à l'écriture de votre premier script Web3 ! Aucune expérience préalable dans le développement de blockchain n'est requise ! -## 1. Créez votre compte Alchemy gratuit {#sign-up-for-a-free-alchemy-account} +## 1. Créez un compte Alchemy gratuit {#sign-up-for-a-free-alchemy-account} -Il est très facile de créer un compte sur Alchemy. [Inscrivez-vous gratuitement ici](https://auth.alchemyapi.io/signup). +Créer un compte sur Alchemy est facile, [inscrivez-vous gratuitement ici](https://auth.alchemy.com/). ## 2. Créer une application Alchemy {#create-an-alchemy-app} -Pour communiquer avec la chaîne Ethereum et utiliser les services d'Alchemy, vous aurez besoin d'une clé d'API pour authentifier vos requêtes. +Pour communiquer avec la chaîne Ethereum et utiliser les produits d'Alchemy, vous avez besoin d'une clé d'API pour authentifier vos requêtes. -Vous pouvez [créer vos clés API depuis le tableau de bord](http://dashboard.alchemyapi.io/). Pour créer un nouvelle clé, cliquez sur « Créer une application » comme indiqué ci-dessous : +Vous pouvez [créer des clés d'API depuis le tableau de bord](https://dashboard.alchemy.com/). Pour créer une nouvelle clé, naviguez vers « Créer une application » comme indiqué ci-dessous : -Un grand merci à [_ShapeShift_](https://shapeshift.com/) _de nous laisser utiliser leur tableau de bord à titre d'exemple !_ +Remerciements spéciaux à [_ShapeShift_](https://shapeshift.com/) _de nous avoir permis de montrer leur tableau de bord !_ -![Tableau de bord Alchemy](./alchemy-dashboard.png) +![Tableau de bord d'Alchemy](./alchemy-dashboard.png) -Remplissez les détails sous « Créer une application » pour obtenir votre nouvelle clé. Vous pouvez également voir les applications que vous avez faites précédemment et celles faites par votre équipe ici. Tirez les clés existantes en cliquant sur « Voir la clé » pour n'importe quelle application. +Remplissez les champs sous « Créer une application » pour obtenir votre nouvelle clé. Vous pouvez également voir ici les applications que vous avez créées précédemment et celles créées par votre équipe. Récupérez les clés existantes en cliquant sur « Voir la clé » pour n'importe quelle application. -![Créer une application avec Alchemy : capture d'écran](./create-app.png) +![Capture d'écran de la création d'application avec Alchemy](./create-app.png) -Vous pouvez également extraire les clés API existantes en passant la souris sur « Apps » et en en sélectionnant une. Vous pouvez « Voir la clé » ici, ainsi que « Modifier l'application » pour mettre des domaines spécifiques sur la liste blanche, voir plusieurs outils de développement et consulter les analyses. +Vous pouvez également récupérer des clés d'API existantes en passant le curseur sur « Apps » et en sélectionnant une. Vous pouvez « Voir la clé » ici, ainsi que « Modifier l'application » pour ajouter des domaines spécifiques à la liste blanche, voir plusieurs outils de développement et consulter les analyses. -![Gif montrant à un utilisateur comment tirer des clés d'API](./pull-api-keys.gif) +![GIF montrant comment un utilisateur peut récupérer des clés d'API](./pull-api-keys.gif) -## 3. Faire une demande en ligne de commande {#make-a-request-from-the-command-line} +## 3. Faire une requête depuis la ligne de commande {#make-a-request-from-the-command-line} Interagissez avec la blockchain Ethereum via Alchemy en utilisant JSON-RPC et curl. -Pour les demandes manuelles, nous recommandons d'interagir avec les `JSON-RPC` via des demandes `POST`. Il suffit de passer l'en-tête `Content-Type : application/json` et votre requête comme corps `POST` avec les champs suivants : +Pour les requêtes manuelles, nous recommandons d'interagir avec le `JSON-RPC` via des requêtes `POST`. Il suffit de transmettre l'en-tête `Content-Type: application/json` et votre requête en tant que corps `POST` avec les champs suivants : -- `jsonrpc` : La version de JSON-RPC - actuellement, seule `2.0` est supportée. -- `méthode` : La méthode de l'API de l'ETH. [Voir la référence API.](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) -- `params` : Une liste de paramètres à transmettre à la méthode. -- `id` : L'ID de votre demande. Sera retourné par la réponse afin que vous puissiez garder la trace de la demande à laquelle une réponse appartient. +- `jsonrpc` : la version JSON-RPC (actuellement, seule la version `2.0` est prise en charge). +- `method` : la méthode de l'API ETH. [Voir la référence de l'API.](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) +- `params` : une liste de paramètres à transmettre à la méthode. +- `id` : l'ID de votre requête. Cet ID sera renvoyé dans la réponse afin que vous puissiez savoir à quelle requête elle correspond. -Voici un exemple que vous pouvez exécuter à partir de la ligne de commande pour obtenir le prix actuel du gaz : +Voici un exemple que vous pouvez exécuter depuis la ligne de commande pour récupérer le prix actuel du gaz : ```bash curl https://eth-mainnet.alchemyapi.io/v2/demo \ @@ -63,7 +65,7 @@ curl https://eth-mainnet.alchemyapi.io/v2/demo \ -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":73}' ``` -_**REMARQUE :** Remplacez [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) par votre propre clé API `https://eth-mainnet.alchemyapi.io/v2/**votre-cle-api`._ +_**NOTE :** Remplacez [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) par votre propre clé d'API `https://eth-mainnet.alchemyapi.io/v2/**votre-clé-api`._ **Résultats :** @@ -73,13 +75,13 @@ _**REMARQUE :** Remplacez [https://eth-mainnet.alchemyapi.io/v2/demo](https://et ## 4. Configurer votre client Web3 {#set-up-your-web3-client} -**Si vous avez un client existant,** changez votre URL actuelle de fournisseur de nœuds en une URL Alchemy avec votre clé API : `"https://eth-mainnet.alchemyapi.io/v2/your-api-key"` +**Si vous avez un client existant,** remplacez l'URL de votre fournisseur de nœud actuel par une URL Alchemy avec votre clé d'API : `"https://eth-mainnet.alchemyapi.io/v2/votre-clé-api"` -**_NOTE :_** Les scripts ci-dessous doivent être exécutés dans un **contexte de nœud** ou **sauvegardé dans un fichier**, et non exécutés en ligne de commande. Si vous n'avez pas encore installé Node ou npm, consultez ce rapide [guide d'installation pour macs](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs). +**_NOTE :** Les scripts ci-dessous doivent être exécutés dans un **contexte de nœud** ou **enregistrés dans un fichier**, et non depuis la ligne de commande. Si Node ou npm ne sont pas déjà installés, consultez ce [guide d'installation rapide pour Mac](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs). -De nombreuses [bibliothèques Web3](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries) peuvent être intégrées à Alchemy, cependant, nous recommandons d'utiliser [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), un remplacement drop-in pour Web3.js, construit et configuré pour fonctionner de façon transparente avec Alchemy. Cela présente de nombreux avantages, comme les tentatives automatiques et la prise en charge robuste de WebSocket. +Il existe de nombreuses [bibliothèques Web3](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries) que vous pouvez intégrer à Alchemy. Cependant, nous vous recommandons d'utiliser [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), un remplacement direct de web3.js, conçu et configuré pour fonctionner de manière transparente avec Alchemy. Cela offre de multiples avantages, tels que des tentatives de reconnexion automatiques et une prise en charge robuste des WebSockets. -Pour installer AlchemyWeb3.js, **accédez au répertoire de votre projet** et exécutez : +Pour installer AlchemyWeb3.js, **accédez au répertoire de votre projet** et exécutez la commande suivante : **Avec Yarn :** @@ -93,62 +95,62 @@ yarn add @alch/alchemy-web3 npm install @alch/alchemy-web3 ``` -Pour interagir avec l'infrastructure de nœuds d'Alchemy, exécutez en NodeJS ou ajoutez ceci à un fichier JavaScript : +Pour interagir avec l'infrastructure de nœuds d'Alchemy, exécutez la commande dans NodeJS ou ajoutez ce qui suit à un fichier JavaScript : ```js const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3( - "https://eth-mainnet.alchemyapi.io/v2/your-api-key" + "https://eth-mainnet.alchemyapi.io/v2/votre-clé-api" ) ``` ## 5. Écrivez votre premier script Web3 ! {#write-your-first-web3-script} -Maintenant pour nous salir les mains avec un peu de programmation Web3, nous allons écrire un script simple qui affiche le dernier numéro de bloc du réseau principal Ethereum. +Maintenant, pour mettre la main à la pâte avec un peu de programmation web3, nous allons écrire un script simple qui affiche le dernier numéro de bloc du réseau principal Ethereum. -**1. Si vous ne l'avez pas déjà fait, dans votre terminal, créez un nouveau répertoire de projet et cd dedans :** +\*\*1. **Si ce n'est pas déjà fait, créez un nouveau répertoire de projet dans votre terminal et accédez-y avec la commande `cd` :** ``` mkdir web3-example cd web3-example ``` -**2. Installez la dépendance Alchemy Web3 (ou toute dépendance Web3) dans votre projet si vous ne l'avez pas déjà fait :** +\*\*2. **Installez la dépendance Alchemy Web3 (ou toute autre dépendance Web3) dans votre projet si ce n'est pas déjà fait :** ``` npm install @alch/alchemy-web3 ``` -**3. Créez un fichier nommé `index.js` et ajoutez le contenu suivant :** +\*\*3. **Créez un fichier nommé `index.js` et ajoutez-y le contenu suivant :** -> Vous devrez remplacer `demo` avec votre clé API Alchemy HTTP. +> Vous devrez à terme remplacer `demo` par votre clé d'API HTTP Alchemy. ```js async function main() { const { createAlchemyWeb3 } = require("@alch/alchemy-web3") const web3 = createAlchemyWeb3("https://eth-mainnet.alchemyapi.io/v2/demo") const blockNumber = await web3.eth.getBlockNumber() - console.log("The latest block number is " + blockNumber) + console.log("Le dernier numéro de bloc est " + blockNumber) } main() ``` -Vous n'êtes pas familiarisé avec l'asynchronisme ? Consultez ce [Medium post](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c). +Vous n'êtes pas familier avec le concept d'asynchronisme ? Consultez cet [article de Medium](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c). -**4. Exécutez-le dans votre terminal en utilisant le nœud** +\*\*4. **Exécutez-le dans votre terminal à l'aide de node :** ``` node index.js ``` -**5. Vous devriez maintenant voir apparaître le dernier numéro de bloc dans votre console !** +\*\*5. **Vous devriez maintenant voir le dernier numéro de bloc s'afficher dans votre console !** ``` -The latest block number is 11043912 +Le dernier numéro de bloc est 11043912 ``` -**Wahou ! Félicitations ! Vous venez de rédiger votre premier script Web3 avec Alchemy 🎉** +**Bravo !** Félicitations ! **Vous venez d'écrire votre premier script web3 avec Alchemy 🎉** -Vous ne savez pas quoi faire ensuite ? Essayez de déployer votre premier contrat intelligent et mettez la main à la pâte avec un peu de programmation Solidity dans notre [Guide de contrats intelligents « Hello World »](https://docs.alchemyapi.io/tutorials/hello-world-smart-contract), ou testez vos connaissances avec le [Tableau de bord de démonstration](https://docs.alchemyapi.io/tutorials/demo-app) ! +Vous n'êtes pas sûr de la prochaine étape ? Essayez de déployer votre premier contrat intelligent et de vous familiariser avec la programmation Solidity dans notre [guide sur les contrats intelligents Hello World](https://www.alchemy.com/docs/hello-world-smart-contract), ou testez vos connaissances sur le tableau de bord avec l'[application de démonstration du tableau de bord](https://docs.alchemyapi.io/tutorials/demo-app) ! -_[S'inscrire gratuitement à Alchemy](https://auth.alchemyapi.io/signup), consulter notre [documentation](https://docs.alchemyapi.io/), et pour les dernières nouvelles, nous suivre sur [Twitter](https://twitter.com/AlchemyPlatform)_. +_[Inscrivez-vous gratuitement sur Alchemy](https://auth.alchemy.com/), consultez notre [documentation](https://www.alchemy.com/docs/) et, pour connaître les dernières actualités, suivez-nous sur [Twitter](https://twitter.com/AlchemyPlatform)_. diff --git a/public/content/translations/fr/developers/tutorials/guide-to-smart-contract-security-tools/index.md b/public/content/translations/fr/developers/tutorials/guide-to-smart-contract-security-tools/index.md index f0f08208a73..07d00faf0b3 100644 --- a/public/content/translations/fr/developers/tutorials/guide-to-smart-contract-security-tools/index.md +++ b/public/content/translations/fr/developers/tutorials/guide-to-smart-contract-security-tools/index.md @@ -1,105 +1,102 @@ --- -title: Un guide des outils de sécurité pour les contrats intelligents -description: Un aperçu de trois différentes techniques de test et d'analyse de programme +title: "Un guide des outils de sécurité pour les contrats intelligents" +description: "Un aperçu de trois différentes techniques de test et d'analyse de programme" author: "Trailofbits" lang: fr -tags: - - "solidity" - - "contrats intelligents" - - "sécurité" +tags: [ "solidité", "contrats intelligents", "sécurité" ] skill: intermediate published: 2020-09-07 -source: Créer des contrats sécurisés +source: Building secure contracts sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis --- -Nous allons utiliser trois techniques distinctes de test et d'analyse de programme : +Nous allons utiliser trois techniques de test et d'analyse de programme distinctes : -- **Analyse statique avec [Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/).** Tous les chemins du programme sont approximés et analysés en même temps, à travers différentes présentations du programme (ex. control-flow-graph) +- **Analyse statique avec [Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/).** Tous les chemins du programme sont approchés et analysés en même temps, à travers différentes présentations du programme (p. ex., graphe de contrôle de flux) - **Fuzzing avec [Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/).** Le code est exécuté avec une génération pseudo-aléatoire de transactions. Le fuzzer tentera de trouver une séquence de transactions pour violer une propriété donnée. - **Exécution symbolique avec [Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/).** Une technique de vérification formelle qui traduit chaque chemin d'exécution en une formule mathématique, sur laquelle on pourra vérifier les contraintes. -Chaque technique comporte ses avantages et ses inconvénients, et trouve son utilité dans des [situations spécifiques](#determining-security-properties) : +Chaque technique a ses avantages et ses inconvénients, et sera utile dans des [cas spécifiques](#determining-security-properties) : -| Technique | Outil | Utilisation | Rapidité | Bogues manqués | Faux positifs | -| ------------------ | --------- | ----------------------------- | --------- | -------------- | ------------- | -| Analyse statique | Slither | CLI & scripts | secondes | modérément | faible | -| Fuzzing | Echidna | Propriétés Solidity | minutes | rarement | aucun | -| Symbolic Execution | Manticore | Propriétés Solidity & scripts | en heures | aucun\* | aucun | +| Technique | Outil | Utilisation | Rapidité | Bogues manqués | Faux positifs | +| -------------------- | --------- | ------------------------------ | --------- | -------------- | ------------- | +| Analyse statique | Slither | CLI et scripts | secondes | modéré | faible | +| Fuzzing | Echidna | Propriétés Solidity | minutes | faible | aucun | +| Exécution symbolique | Manticore | Propriétés Solidity et scripts | en heures | aucun\* | aucun | -\* si tous les chemins sont analysés sans coupure +\* si tous les chemins sont explorés sans expiration de délai -**Slither** analyse les contrats en quelques secondes, cependant, une analyse statique peut conduire à de fausses alertes et sera moins appropriée pour des vérifications complexes (ex. vérifications arithmétiques). Exécutez Slither via l'API pour accéder aux détecteurs intégrés ou via l'API pour des vérifications définies par l'utilisateur. +**Slither** analyse les contrats en quelques secondes. Cependant, l'analyse statique peut générer des faux positifs et sera moins adaptée aux vérifications complexes (p. ex., les vérifications arithmétiques). Exécutez Slither via l'API pour un accès en un clic aux détecteurs intégrés ou via l'API pour des vérifications définies par l'utilisateur. -**Echidna** a besoin de travailler pendant plusieurs minutes et ne produira que de vrais positifs. Echidna vérifie les propriétés de sécurité fournies par les utilisateurs et écrites en Solidity. Il peut rater des bogues dans la mesure où il est basé sur une exploration aléatoire. +**Echidna** doit s'exécuter pendant plusieurs minutes et ne produit que de vrais positifs. Echidna vérifie les propriétés de sécurité fournies par l'utilisateur, écrites en Solidity. Il peut passer à côté de bogues, car il est basé sur une exploration aléatoire. -**Manticore** effectue l'analyse la plus poussée. Comme Echidna, Manticore vérifie les propriétés fournies par l'utilisateur. Il lui faudra plus de temps pour fonctionner, mais peut approuver la validité d'une propriété et ne signalera pas de fausses alertes. +**Manticore** effectue l'analyse la plus poussée. Comme Echidna, Manticore vérifie les propriétés fournies par l'utilisateur. Son exécution prendra plus de temps, mais il peut prouver la validité d'une propriété et ne signalera pas de faux positifs. -## Flux de travail conseillé {#suggested-workflow} +## Flux de travail suggéré {#suggested-workflow} -Commencez avec les détecteurs intégrés de Slither pour vous assurer qu'aucun bogue mineur n'est présent ou ne sera introduit plus tard. Utilisez Slither pour vérifier les propriétés liées aux héritages, aux dépendances variables et aux problèmes structurels. Au fur et à mesure que le code base grossit, utilisez Echidna pour tester des propriétés plus complexes de la machine d'état. Revisitez Slither pour développer des contrôles personnalisés pour les protections non présentes dans Solidity comme la protection contre le remplacement d'une fonction. Enfin, utiliser Manticore pour effectuer des vérifications ciblées de propriétés critiques de sécurité, par exemple des opérations arithmétiques. +Commencez par les détecteurs intégrés de Slither pour vous assurer qu'aucun bogue simple n'est présent ou ne sera introduit ultérieurement. Utilisez Slither pour vérifier les propriétés liées à l'héritage, aux dépendances entre les variables et aux problèmes structurels. À mesure que la base de code s'agrandit, utilisez Echidna pour tester les propriétés plus complexes de la machine d'état. Revenez à Slither pour développer des vérifications personnalisées pour des protections non disponibles dans Solidity, comme la protection contre la redéfinition d'une fonction. Enfin, utilisez Manticore pour effectuer une vérification ciblée des propriétés de sécurité critiques, p. ex., les opérations arithmétiques. -- Utilisez le CLI de Slither pour relever les problèmes courants -- Utilisez Echidna pour tester les propriétés de sécurité de haut niveau dans votre contrat +- Utilisez la CLI de Slither pour détecter les problèmes courants +- Utilisez Echidna pour tester les propriétés de sécurité de haut niveau de votre contrat - Utilisez Slither pour écrire des vérifications statiques personnalisées -- Utilisez Manticore pour vous assurer de façon approfondie des propriétés critiques de sécurité +- Utilisez Manticore lorsque vous souhaitez une assurance approfondie des propriétés de sécurité critiques -**Note sur les tests unitaires**. Les tests unitaires sont nécessaires pour construire des logiciels de haute qualité. Cependant, ces techniques ne sont pas les mieux adaptées pour trouver des failles de sécurité. Ils sont généralement utilisés pour tester les comportements positifs du code (c.-à-d. que le code fonctionne comme prévu dans un contexte normal), tandis que les défauts de sécurité tendent à résider dans des cas particuliers que les développeurs ne considéraient pas. Dans notre étude de dizaines d'examens sur la sécurité des contrats intelligents, [la couverture des tests unitaires n'a eu aucun effet sur le nombre ou la gravité des failles de sécurité](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/) que nous avons trouvés dans le code de notre client. +**Remarque sur les tests unitaires**. Les tests unitaires sont nécessaires pour créer des logiciels de haute qualité. Cependant, ces techniques ne sont pas les plus adaptées pour trouver des failles de sécurité. Ils sont généralement utilisés pour tester les comportements positifs du code (c'est-à-dire que le code fonctionne comme prévu dans le contexte normal), tandis que les failles de sécurité ont tendance à se trouver dans des cas limites que les développeurs n'ont pas envisagés. Dans notre étude portant sur des dizaines d'audits de sécurité de contrats intelligents, [la couverture des tests unitaires n'a eu aucun effet sur le nombre ou la gravité des failles de sécurité](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/) que nous avons trouvées dans le code de nos clients. ## Détermination des propriétés de sécurité {#determining-security-properties} -Pour tester et vérifier efficacement votre code, vous devez identifier les zones qui nécessitent une attention particulière. Comme vos ressources consacrées à la sécurité sont limitées, il est important d’optimiser vos efforts pour déterminer la portée des parties faibles ou de grande valeur de votre code base. La modélisation des menaces peut vous aider. Envisagez la révision : +Pour tester et vérifier efficacement votre code, vous devez identifier les zones qui nécessitent une attention particulière. Vos ressources en matière de sécurité étant limitées, il est important de cibler les parties faibles ou à forte valeur de votre base de code afin d'optimiser vos efforts. La modélisation des menaces peut vous aider. Envisagez de consulter : -- [Évaluation Rapide des Risques](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (notre approche préférée lorsque le temps se fait court) -- [Guide de modélisation des menaces du système centralisé de données](https://csrc.nist.gov/publications/detail/sp/800-154/draft) (aka NIST 800-154) -- [Modélisation des fils de discussion Shostack](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) -- [STRIDE](https://wikipedia.org/wiki/STRIDE_(security)) / [DREAD](https://wikipedia.org/wiki/DREAD_(risk_assessment_model)) +- [Rapid Risk Assessments](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (notre approche privilégiée lorsque le temps est limité) +- [Guide to Data-Centric System Threat Modeling](https://csrc.nist.gov/pubs/sp/800/154/ipd) (alias NIST 800-154) +- [Shostack threat modeling](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) +- [STRIDE](https://wikipedia.org/wiki/STRIDE_\(security\)) / [DREAD](https://wikipedia.org/wiki/DREAD_\(risk_assessment_model\)) - [PASTA](https://wikipedia.org/wiki/Threat_model#P.A.S.T.A.) -- [Utilisation des affirmations](https://blog.regehr.org/archives/1091) +- [Use of Assertions](https://blog.regehr.org/archives/1091) ### Composants {#components} Savoir ce que vous voulez vérifier vous aidera également à sélectionner le bon outil. -Les grands domaines qui sont souvent pertinents pour les contrats intelligents comprennent : +Les grands domaines qui sont souvent pertinents pour les contrats intelligents sont les suivants : -- **Machine d'état.** La plupart des contrats peuvent être représentés comme une machine d’état. Pensez à vérifier que (1) aucun état non valide ne peut être atteint, (2) si un état est valide, qu’il peut être atteint, et (3) aucun état ne piège le contrat. +- **Machine d'état.** La plupart des contrats peuvent être représentés comme une machine d'état. Envisagez de vérifier que (1) aucun état non valide ne peut être atteint, (2) si un état est valide, il peut être atteint, et (3) aucun état ne bloque le contrat. - - Echidna et Manticore sont les outils à privilégier pour tester les spécifications des machines d’état. + - Echidna et Manticore sont les outils à privilégier pour tester les spécifications des machines d'état. -- **Contrôle d'accès.** Si votre système a des utilisateurs dotés de privilèges (par exemple, un propriétaire, des contrôleurs, ...) vous devez vous assurer que (1) chaque utilisateur ne peut effectuer que les actions autorisées et (2) aucun utilisateur ne peut bloquer les actions d’un utilisateur avec des privilèges supérieurs. +- **Contrôles d'accès.** Si votre système a des utilisateurs privilégiés (p. ex., un propriétaire, des contrôleurs, etc.) vous devez vous assurer que (1) chaque utilisateur ne peut effectuer que les actions autorisées et (2) aucun utilisateur ne peut bloquer les actions d'un utilisateur plus privilégié. - - Slither, Echidna et Manticore peuvent vérifier les contrôles d’accès corrects. Par exemple, Slither peut vérifier que seules les fonctions de la liste blanche ne disposent pas du modificateur onlyOwner. Echidna et Manticore sont utiles pour un contrôle d’accès plus complexe, comme une autorisation donnée uniquement si le contrat atteint un état donné. + - Slither, Echidna et Manticore peuvent vérifier la correction des contrôles d'accès. Par exemple, Slither peut vérifier que seules les fonctions sur liste blanche n'ont pas le modificateur onlyOwner. Echidna et Manticore sont utiles pour un contrôle d'accès plus complexe, comme une autorisation accordée uniquement si le contrat atteint un état donné. -- **Opérations arithmétiques.** Vérifier la solidité des opérations arithmétiques est absolument essentiel. L’utilisation de `SafeMath` partout est une bonne étape pour éviter les dépassements supérieurs et inférieurs, cependant, vous devez toujours prendre en compte d’autres défauts arithmétiques, y compris les problèmes d’arrondi et les défauts qui piègent le contrat. +- **Opérations arithmétiques.** La vérification de la justesse des opérations arithmétiques est essentielle. L'utilisation de `SafeMath` partout est une bonne mesure pour empêcher les dépassements/soupassements de capacité. Cependant, vous devez toujours tenir compte des autres défauts arithmétiques, y compris les problèmes d'arrondi et les défauts qui bloquent le contrat. - - Manticore est ici le meilleur choix. Echidna peut être utilisé si l’arithmétique est hors du champ d’application du solveur SMT. + - Manticore est ici le meilleur choix. Echidna peut être utilisé si l'arithmétique est hors du champ d'application du solveur SMT. -- **Exactitude de l'héritage.** Les contrats de solidity reposent fortement sur l’héritage multiple. Des erreurs telles qu’une fonction d’ombrage manquant d’un appel `super` et un ordre de linéarisation c3 mal interprété peuvent facilement être introduites. +- **Correction de l'héritage.** Les contrats Solidity reposent fortement sur l'héritage multiple. Des erreurs telles qu'une fonction masquée omettant un appel `super` et un ordre de linéarisation C3 mal interprété peuvent facilement être introduites. - - Slither est l’outil pour assurer la détection de ces problèmes. + - Slither est l'outil qui permet de garantir la détection de ces problèmes. -- **Interactions externes.** Les contrats interagissent les uns avec les autres, et certains contrats externes ne doivent pas être approuvés. Par exemple, si votre contrat repose sur des oracles externes, restera-t-il sécurisé si la moitié des oracles disponibles sont compromis ? +- **Interactions externes.** Les contrats interagissent les uns avec les autres, et il ne faut pas faire confiance à certains contrats externes. Par exemple, si votre contrat dépend d'oracles externes, restera-t-il sécurisé si la moitié des oracles disponibles sont compromis ? - - Manticore et Echidna sont le meilleur choix pour tester les interactions externes avec vos contrats. Manticore dispose d’un mécanisme intégré pour talonner les contrats externes. + - Manticore et Echidna sont le meilleur choix pour tester les interactions externes avec vos contrats. Manticore dispose d'un mécanisme intégré pour simuler les contrats externes. -- **Conformité standard.** Les normes Ethereum (par exemple ERC20) ont un historique de défauts dans leur conception. Soyez conscient des limites de la norme sur laquelle vous vous appuyez. - - Slither, Echidna et Manticore vous aideront à détecter les écarts par rapport à une norme donnée. +- **Conformité aux standards.** Les standards Ethereum (p. ex., ERC20) ont un historique de failles dans leur conception. Soyez conscient des limites du standard sur lequel vous vous basez. + - Slither, Echidna et Manticore vous aideront à détecter les écarts par rapport à un standard donné. -### Fiche mémo de sélection d’outils {#tool-selection-cheatsheet} +### Aide-mémoire de sélection d'outils {#tool-selection-cheatsheet} -| Composant | Outils | Exemples | -| ----------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Machine d'état | Echidna, Manticore | | -| Access control | Slither, Echidna, Manticore | [Slither exercise 2](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md), [Echidna exercise 2](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | -| Arithmetic operations | Manticore, Echidna | [Echidna exercise 1](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md), [Manticore exercises 1 - 3](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | -| Inheritance correctness | Slither | [Slither exercise 1](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | -| External interactions | Manticore, Echidna | | -| Standard conformance | Slither, Echidna, Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | +| Composant | Outils | Exemples | +| ------------------------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Machine d'état | Echidna, Manticore | | +| Contrôle d'accès | Slither, Echidna, Manticore | [Slither exercise 2](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md), [Echidna exercise 2](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | +| Opérations arithmétiques | Manticore, Echidna | [Echidna exercise 1](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md), [Manticore exercises 1 - 3](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | +| Correction de l'héritage | Slither | [Slither exercise 1](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | +| Interactions externes | Manticore, Echidna | | +| Conformité aux standards | Slither, Echidna, Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | -D’autres domaines devront être vérifiés en fonction de vos objectifs, mais ces domaines généraux représentent un bon point de départ pour tout système de contrat intelligent. +D'autres domaines devront être vérifiés en fonction de vos objectifs, mais ces grands axes de travail constituent un bon point de départ pour tout système de contrat intelligent. -Nos audits publics contiennent des exemples de propriétés vérifiées ou testées. Envisagez de lire les sections `Test et vérification automatisées` des rapports suivants pour examiner les propriétés de sécurité dans le réel : +Nos audits publics contiennent des exemples de propriétés vérifiées ou testées. Pensez à lire les sections `Test et vérification automatisés` des rapports suivants pour examiner des propriétés de sécurité du monde réel : - [0x](https://github.com/trailofbits/publications/blob/master/reviews/0x-protocol.pdf) - [Balancer](https://github.com/trailofbits/publications/blob/master/reviews/BalancerCore.pdf) diff --git a/public/content/translations/fr/developers/tutorials/hello-world-smart-contract-fullstack/index.md b/public/content/translations/fr/developers/tutorials/hello-world-smart-contract-fullstack/index.md index 649e554f42d..b9947928805 100644 --- a/public/content/translations/fr/developers/tutorials/hello-world-smart-contract-fullstack/index.md +++ b/public/content/translations/fr/developers/tutorials/hello-world-smart-contract-fullstack/index.md @@ -1,106 +1,93 @@ --- -title: Un Contrat intelligent « Hello World » pour les débutants - Fullstack -description: Tutoriel d'introduction à l'écriture et au déploiement d'un contrat intelligent simple sur Ethereum. +title: "Un contrat intelligent « Hello World » pour les débutants - Fullstack" +description: "Tutoriel d'introduction à l'écriture et au déploiement d'un contrat intelligent simple sur Ethereum." author: "nstrike2" tags: - - "solidity" - - "hardhat" - - "alchemy" - - "contrats intelligents" - - "déployer" - - "explorateur de blockchain" - - "frontend" - - "transactions" + [ + "solidité", + "hardhat", + "alchemy", + "contrats intelligents", + "déploiement", + "Explorateur de bloc", + "frontend", + "transactions" + ] skill: beginner lang: fr published: 2021-10-25 --- -Ce guide s'adresse à vous si vous êtes novice en matière de développement blockchain et que vous ne savez pas par où commencer ou comment déployer et interagir avec les contrats intelligents. Nous allons parcourir la création et le déploiement d'un contrat intelligent simple sur le réseau de test de Goerli à l'aide de [MetaMask](https://metamask.io), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org), et [Alchemy](https://alchemyapi.io/eth) . +Ce guide s'adresse à vous si vous êtes novice en matière de développement blockchain et que vous ne savez pas par où commencer ou comment déployer et interagir avec les contrats intelligents. Nous allons vous guider dans la création et le déploiement d'un contrat intelligent simple sur le réseau de test Goerli en utilisant [MetaMask](https://metamask.io), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org), et [Alchemy](https://alchemy.com/eth). -Vous aurez besoin d'un compte Alchemy pour achever ce tutoriel. [S'inscrire pour un compte gratuit](https://www.alchemy.com/). +Vous aurez besoin d'un compte Alchemy pour achever ce tutoriel. [Inscrivez-vous pour un compte gratuit](https://www.alchemy.com/). -Si vous avez des questions à un moment ou à un autre, n'hésitez pas à en discuter sur le [Discord Alchemy](https://discord.gg/gWuC7zB)! +Si vous avez des questions à un moment ou à un autre, n'hésitez pas à nous contacter sur le [Discord d'Alchemy](https://discord.gg/gWuC7zB) ! ## Partie 1 - Créer et déployer votre contrat intelligent avec Hardhat {#part-1} ### Se connecter au réseau Ethereum {#connect-to-the-ethereum-network} -Il existe de nombreuses façons de faire des requêtes dans la chaîne d'Ethereum. Pour plus de simplicité, nous allons utiliser un compte gratuit sur Alchemy, une plateforme de blockchain et d'API pour développeur, nous permettant de communiquer avec la chaîne Ethereum sans avoir à exécuter notre propre nœud. Alchemy dispose également d'outils de développement pour la surveillance et l'analyse, dont nous allons tirer parti dans ce tutoriel, pour comprendre ce qui se passe sous le capot dans le déploiement de notre contrat intelligent. +Il existe de nombreuses façons de faire des requêtes sur la chaîne Ethereum. Pour plus de simplicité, nous allons utiliser un compte gratuit sur Alchemy, une plateforme de développement blockchain et une API qui nous permet de communiquer avec la chaîne Ethereum sans avoir à exécuter notre propre nœud. Alchemy dispose également d'outils de développement pour la surveillance et l'analyse, dont nous allons tirer parti dans ce tutoriel, pour comprendre ce qui se passe sous le capot dans le déploiement de notre contrat intelligent. ### Créez votre application et votre clé API {#create-your-app-and-api-key} -Une fois votre compte Alchemy créé, vous pouvez générer une clé API en créant une application. Cela va vous permettre d'émettre des requêtes sur le réseau de test Goerli. Si vous n'êtes pas familiarisé avec les réseaux de test, vous pouvez [lire le guide d'Alchemy sur le choix d'un réseau](https://docs.alchemyapi.io/guides/choosing-a-network). +Une fois votre compte Alchemy créé, vous pouvez générer une clé API en créant une application. Cela va vous permettre d'émettre des requêtes sur le réseau de test Goerli. Si vous n'êtes pas familier avec les réseaux de test, vous pouvez [lire le guide d'Alchemy pour choisir un réseau](https://www.alchemy.com/docs/choosing-a-web3-network). -Sur le tableau de bord Alchemy, trouvez le menu déroulant **Apps** dans la barre de navigation et cliquez sur **Créer une application**. +Sur le tableau de bord Alchemy, trouvez le menu déroulant **Apps** dans la barre de navigation et cliquez sur **Create App**. -![créer une application Hello world](./hello-world-create-app.png) +![Création de l'application Hello world](./hello-world-create-app.png) -Donnez à votre application le nom "_Hello World_" et écrivez une courte description. Sélectionnez **Staging** comme environnement et **Goerli** comme réseau. +Donnez à votre application le nom « _Hello World_ » et écrivez une courte description. Sélectionnez **Staging** comme environnement et **Goerli** comme réseau. -![créer une vue de l'application Hello world](./create-app-view-hello-world.png) +![Vue de création de l'application Hello world](./create-app-view-hello-world.png) _Note : assurez-vous de sélectionner **Goerli**, sinon ce tutoriel ne fonctionnera pas._ -Cliquer sur **Create app**. Votre application apparaîtra dans le tableau ci-dessous. +Cliquez sur **Créer une application**. Votre application apparaîtra dans le tableau ci-dessous. ### Créer un compte Ethereum {#create-an-ethereum-account} Vous avez besoin d'un compte Ethereum pour envoyer et recevoir des transactions. Nous utiliserons MetaMask, un portefeuille virtuel intégré au navigateur permettant aux utilisateurs de gérer l'adresse de leur compte 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 Goerli » en haut à droite (afin de ne pas utiliser d'argent réel). +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 le « Réseau de test Goerli » en haut à droite (afin de ne pas utiliser d'argent réel). -### É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 votre contrat intelligent sur le réseau de test, vous aurez besoin de faux ETH. Pour obtenir de l'ETH sur le réseau Goerli, rendez-vous sur un robinet Goerli et entrez l'adresse de votre compte Goerli. Notez que les robinets Goerli peuvent avoir quelques difficultés de fonctionnement ces derniers temps - consultez la [page des réseaux de test](/developers/docs/networks/#goerli) pour une liste d'options à essayer : -_Note : en raison de la congestion du réseau, cela peut prendre un certain temps._ `` +_Note : en raison de la congestion du réseau, cela peut prendre un certain temps._ +`` ### Étape 5 : Vérifiez votre solde {#step-5-check-your-balance} -Pour revérifier que l'ETH est dans votre portefeuille, créons une requête - -en utilisant [l'outil Composer 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. Pour en savoir plus, consultez [le court tutoriel d'Alchemy sur la manière d'utiliser l'outil Composer](https://youtu.be/r6sjRxBZJuU). - -Entrez votre adresse de compte MetaMask et cliquez sur **Envoyer la demande**. Vous verrez une réponse qui ressemble au morceau de code ci-dessous. - +Pour vérifier que les ETH sont bien dans votre portefeuille, nous allons effectuer une requête [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) en utilisant l'[outil Composer 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. Pour en savoir plus, consultez le [court tutoriel d'Alchemy sur la manière d'utiliser l'outil Composer](https://youtu.be/r6sjRxBZJuU). +Saisissez l'adresse de votre compte MetaMask et cliquez sur **Envoyer la requête**. Vous verrez une réponse qui ressemble à l'extrait de code ci-dessous. ```json { "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } ``` - - - > _Note : Ce résultat est en wei, pas en ETH. Le wei est utilisé comme la plus petite dénomination d'ether._ Ouf ! Notre faux argent est bien là. - - -### Étape 6 : Initialisons notre projet {#step-6-initialize-our-project} +### Étape 6 : Initialiser notre projet {#step-6-initialize-our-project} Tout d'abord, nous devons créer un dossier pour notre projet. Accédez à votre ligne de commande et entrez ce qui suit. - - ``` mkdir hello-world cd hello-world ``` +Maintenant que nous sommes dans notre dossier de projet, nous allons utiliser `npm init` pour initialiser le projet. -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 npm installé, suivez [ces instructions pour installer Node.js et npm](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm). +> Si vous n'avez pas encore installé npm, suivez [ces instructions pour installer Node.js et npm](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm). Pour les besoins de ce tutoriel, peu importe comment vous répondez aux questions d'initialisation. Voici comment nous l'avons fait à titre de référence : - - ``` package name: (hello-world) version: (1.0.0) @@ -127,42 +114,29 @@ About to write to /Users/.../.../.../hello-world/package.json: } ``` +Approuvez le fichier package.json et nous sommes prêts ! -Approuvez le package.json et nous sommes prêts à démarrer ! - - - -### Step 7 : Téléchargez Hardhat {#step-7-download-hardhat} +### Étape 7 : Télécharger Hardhat {#step-7-download-hardhat} Hardhat est un environnement de développement qui permet de compiler, déployer, tester et déboguer votre logiciel Ethereum. Il aide les développeurs à construire des contrats intelligents et des dApps localement avant de les déployer sur la chaîne en production. -À l'intérieur de notre projet `hello-world`, exécutez : - - +Dans notre projet `hello-world`, 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 {#step-8-create-hardhat-project} -Dans le dossier de notre projet`hello-world`, exécutez : - - +Dans le dossier de notre projet `hello-world`, 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 @@ -174,75 +148,65 @@ Vous devriez maintenant voir un message de bienvenue ainsi qu'une option pour s 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 👷‍ +👷 Bienvenue dans Hardhat v2.0.11 👷‍ -What do you want to do? … -Create a sample project -❯ Create an empty hardhat.config.js -Quit +Que voulez-vous faire ? … +Créer un projet d'exemple +❯ Créer un fichier hardhat.config.js vide +Quitter ``` - Cela générera un fichier `hardhat.config.js` dans le projet. Nous l'utiliserons plus tard dans le tutoriel pour spécifier la configuration de notre projet. - - ### Étape 9 : Ajouter les dossiers du projet {#step-9-add-project-folders} -Pour garder le projet organisé, créons deux nouveaux dossiers. Dans la ligne de commande, naviguez vers le répertoire racine de votre projet `hello-world` et tapez : - - +Pour garder le projet organisé, créons deux nouveaux dossiers. Dans la ligne de commande, naviguez vers le répertoire racine de votre projet hello-world et tapez : ``` mkdir contracts mkdir scripts ``` - -- `contrats/` est l'endroit où nous garderons notre fichier de code de contrat intelligent 'hello world' -- `scripts/` est l'endroit où nous garderons les scripts à déployer et pour interagir avec notre contrat - - +- `contracts/` est l'endroit où nous conserverons le fichier de code de notre contrat intelligent hello world. +- `scripts/` est l'endroit où nous conserverons les scripts pour déployer notre contrat et interagir avec lui. ### Étape 10 : Écrire notre contrat {#step-10-write-our-contract} -Vous vous demandez peut-être quand allons-nous enfin écrire du code ? C'est maintenant ! +Vous vous demandez peut-être : quand allons-nous écrire du code ? C'est maintenant ! -Ouvrez le projet hello-world dans votre éditeur préféré. Les contrats intelligents sont le plus souvent écrits en Solidity, que nous utiliserons pour écrire le notre. +Ouvrez le projet hello-world dans votre éditeur préféré. Les contrats intelligents sont le plus souvent écrits en Solidity, que nous utiliserons pour écrire notre contrat intelligent.‌ 1. Naviguez vers le dossier `contracts` et créez un nouveau fichier appelé `HelloWorld.sol` 2. Ci-dessous se trouve un exemple de contrat intelligent Hello World que nous utiliserons pour ce tutoriel. Copiez le contenu ci-dessous dans le fichier `HelloWorld.sol`. _Note : Assurez-vous de lire les commentaires pour comprendre ce que fait ce contrat._ - - ``` -// Specifies the version of Solidity, using semantic versioning. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// Spécifie la version de Solidity, en utilisant le versionnage sémantique. +// En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity >=0.7.3; -// Defines a contract named `HelloWorld`. -// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// Définit un contrat nommé `HelloWorld`. +// Un contrat est une collection de fonctions et de données (son état). Une fois déployé, un contrat réside à une adresse spécifique sur la blockchain Ethereum. En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - //Emitted when update function is called - //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen. + //Émis lorsque la fonction de mise à jour est appelée + //Les événements de contrat intelligent sont un moyen pour votre contrat de communiquer que quelque chose s'est passé sur la blockchain à votre interface d'application, qui peut « écouter » certains événements et prendre des mesures lorsqu'ils se produisent. event UpdatedMessages(string oldStr, string newStr); - // Declares a state variable `message` of type `string`. - // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. + // Déclare une variable d'état `message` de type `string`. + // Les variables d'état sont des variables dont les valeurs sont stockées en permanence dans le stockage du contrat. Le mot-clé `public` rend les variables accessibles depuis l'extérieur d'un contrat et crée une fonction que d'autres contrats ou clients peuvent appeler pour accéder à la valeur. string public message; - // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. - // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + // Semblable à de nombreux langages orientés objet basés sur les classes, un constructeur est une fonction spéciale qui n'est exécutée qu'à la création du contrat. + // Les constructeurs sont utilisés pour initialiser les données du contrat. En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) { - // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). + // Accepte un argument de chaîne `initMessage` et définit la valeur dans la variable de stockage `message` du contrat). message = initMessage; } - // A public function that accepts a string argument and updates the `message` storage variable. + // Une fonction publique qui accepte un argument de chaîne et met à jour la variable de stockage `message`. function update(string memory newMessage) public { string memory oldMsg = message; message = newMessage; @@ -251,79 +215,59 @@ contract HelloWorld { } ``` - Il s'agit d'un contrat intelligent basique qui stocke un message lors de sa création. Il peut être mis à jour en appelant la fonction `update`. +### Étape 11 : Connecter MetaMask et Alchemy à votre projet {#step-11-connect-metamask-alchemy-to-your-project} - -### Étape 11 : Connecter MetaMask & Alchemy à votre projet {#step-11-connect-metamask-alchemy-to-your-project} - -Maintenant que nous avons créé un portefeuille Metamask, un compte Alchemy et écrit notre contrat intelligent, il est temps de connecter les trois. +Nous avons créé un portefeuille MetaMask, un compte Alchemy et rédigé notre contrat intelligent. Il est maintenant temps de connecter les trois. Chaque transaction envoyée depuis votre portefeuille nécessite une signature à l'aide de votre clé privée unique. Pour fournir cette autorisation à notre programme, nous pouvons stocker en toute sécurité notre clé privée dans un fichier d'environnement. Nous y stockerons également une clé API pour Alchemy. - - -> Pour en savoir plus sur l'envoi de transactions, consultez [ce tutoriel](https://docs.alchemyapi.io/alchemy/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](https://www.alchemy.com/docs/hello-world-smart-contract#step-11-connect-metamask--alchemy-to-your-project) 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 du projet. Ajoutez-y votre clé privée MetaMask et l'URL API HTTP d'Alchemy. Votre fichier d'environnement doit être nommé `.env` ou il ne sera pas reconnu comme un fichier d'environnement. -Ne le nommez pas `process.env` ou `.env-custom` ou autrement. +Ne le nommez pas `process.env` ou `.env-custom` ou quoi que ce soit d'autre. - 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 - Voir ci-dessous pour obtenir l'URL de l'API HTTP Alchemy ![](./get-alchemy-api-key.gif) -Votre `.env` devrait ressembler à ceci : - - +Votre `.env` devrait ressembler à ceci : ``` API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" PRIVATE_KEY = "your-metamask-private-key" ``` - Pour les relier à notre code, nous ferons référence à ces variables dans notre fichier `hardhat.config.js` à l'étape 13. - - ### Étape 12 : Installer Ethers.js {#step-12-install-ethersjs} -Ethers.js est une bibliothèque qui permet facilement d'interagir et de faire des requêtes pour Ethereum en conditionnant les méthodes [standard JSON-RPC](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc) avec des méthodes plus conviviales d'utilisation. +Ethers.js est une bibliothèque qui facilite l'interaction et les requêtes vers Ethereum en enveloppant les [méthodes JSON-RPC standard](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc) avec des méthodes plus conviviales. -Hardhat nous permet d'intégrer des [plugins](https://hardhat.org/plugins/) pour des outils supplémentaires et des fonctionnalités étendues. Nous allons tirer parti du [plugin Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) pour le déploiement de contrats. +Hardhat nous permet d'intégrer des [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 de contrats. Dans votre dossier de projet, tapez : - - ```bash npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" ``` - - - ### Étape 13 : Mettre à jour hardhat.config.js {#step-13-update-hardhat-configjs} -A ce stade, nous avons ajouté plusieurs dépendances et plugins. Nous devons maintenant mettre à jour `hardhat.config.js` pour que notre projet les reconnaisse. - -Mettez à jour votre `hardhat.config.js` pour qu'il ressemble à ceci : - +À ce stade, nous avons ajouté plusieurs dépendances et plugins. Nous devons maintenant mettre à jour `hardhat.config.js` pour que notre projet les reconnaisse. +Mettez à jour votre `hardhat.config.js` pour qu'il ressemble à ceci : ```javascript /** @@ -348,25 +292,17 @@ module.exports = { } ``` - - - ### Étape 14 : Compiler notre contrat {#step-14-compile-our-contract} -Pour s’assurer à ce stade que tout fonctionne, compilons notre contrat. La tâche `compile` est une des tâches intégrées à hardhat. +Pour s’assurer à ce stade que tout fonctionne, compilons notre contrat. La tâche `compile` est l'une des tâches intégrées de hardhat. À partir de la ligne de commande, exécutez : - - ```bash 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 recevoir un avertissement concernant l'« identifiant de licence SPDX non fourni dans le fichier source », mais ne vous inquiétez pas — espérons que tout le reste se passe bien ! Sinon, vous pouvez toujours envoyer un message sur le [Discord d'Alchemy](https://discord.gg/u72VCg3). ### Étape 15 : Écrire notre script de déploiement {#step-15-write-our-deploy-script} @@ -374,15 +310,13 @@ Maintenant que notre contrat est codé et que notre fichier de configuration est Naviguez vers le dossier `scripts/` et créez un nouveau fichier appelé `deploy.js`, en y ajoutant le contenu suivant : - - ```javascript async function main() { const HelloWorld = await ethers.getContractFactory("HelloWorld") - // 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 hello_world = await HelloWorld.deploy("Hello World!") - console.log("Contract deployed to address:", hello_world.address) + console.log("Contrat déployé à l'adresse :", hello_world.address) } main() @@ -393,52 +327,37 @@ 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. ```javascript const HelloWorld = await ethers.getContractFactory("HelloWorld") ``` - -Une `ContractFactory` dans ethers.js est une abstraction utilisée pour déployer de nouveaux contrats intelligents. Ainsi, `HelloWorld` est ici une [usine](https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)) pour des exemples de notre contrat Hello world. Lors de l'utilisation du plugin `hardhat-ethers`, les instances `ContractFactory` et `Contract` sont connectées par défaut au premier signataire (propriétaire). - - +Un `ContractFactory` dans ethers.js est une abstraction utilisée pour déployer de nouveaux contrats intelligents, donc `HelloWorld` ici est une [factory (fabrique)](https://en.wikipedia.org/wiki/Factory_\(object-oriented_programming\)) pour les instances de notre contrat hello world. Lors de l'utilisation du plugin `hardhat-ethers`, les instances `ContractFactory` et `Contract` sont connectées par défaut au premier signataire (propriétaire). ```javascript const hello_world = await HelloWorld.deploy() ``` - -Appeler `deploy()` sur un `ContractFactory` va démarrer le déploiement et retourner une `Promise` qui corrige l'objet `Contract`. C'est l'objet qui a une méthode pour chacune de nos fonctions de contrat intelligent. - - +Appeler `deploy()` sur une `ContractFactory` lancera le déploiement et retournera une `Promise` qui se résout en un objet `Contract`. C'est l'objet qui possède une méthode pour chacune des fonctions de notre contrat intelligent. ### Étape 16 : Déployer notre contrat {#step-16-deploy-our-contract} Nous sommes enfin prêts à déployer notre contrat intelligent ! Naviguez vers la ligne de commande et exécutez : - - ```bash npx hardhat run scripts/deploy.js --network goerli ``` - -Vous devriez dès lors voir quelque chose comme : - - +Vous devriez maintenant voir quelque chose comme : ```bash -Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +Contrat déployé à l'adresse : 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 ``` - **Veuillez sauvegarder cette adresse**. Nous l'utiliserons plus tard dans le tutoriel. -Si nous allons sur l'[etherscan Goerli](https://goerli.etherscan.io) et que nous recherchons l'adresse de notre contrat, nous devrions constater qu'il a été déployé avec succès. La transaction ressemblera à ceci : +Si nous allons sur [l'Etherscan de Goerli](https://goerli.etherscan.io) et que nous recherchons l'adresse de notre contrat, nous devrions être en mesure de voir qu'il a été déployé avec succès. La transaction ressemblera à quelque chose comme : ![](./etherscan-contract.png) @@ -446,30 +365,24 @@ L'adresse `From` devrait correspondre à l'adresse de votre compte MetaMask et l ![](./etherscan-transaction.png) -Félicitations ! Vous venez de déployer un contrat intelligent sur la chaîne de test d'Ethereum. +Félicitations ! Vous venez de déployer un contrat intelligent sur un réseau de test 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, assurez-vous de filtrer par application et sélectionnez **Hello World**. +Pour comprendre ce qui se passe en coulisses, naviguons vers l'onglet Explorer dans notre [tableau de bord Alchemy](https://dashboard.alchemy.com/explorer). Si vous avez plusieurs applications Alchemy, assurez-vous de filtrer par application et de sélectionner **Hello World**. ![](./hello-world-explorer.png) -Ici, vous verrez un certain nombre de méthodes JSON-RPC que Hardhat/Ethers a réalisés sous le capot pour nous lorsque nous avons appelé la fonction `.deploy()`. Ici, deux méthodes importantes sont [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), qui est la demande d'écriture de notre contrat sur la chaîne Goerli, et [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash) qui est une requête pour lire des informations sur notre transaction compte tenu du hachage. Pour en savoir plus sur l'envoi de transactions, consultez [notre tutoriel sur l'envoi de transactions avec web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). - - +Ici, vous verrez une poignée de méthodes JSON-RPC que Hardhat/Ethers ont créées en coulisses pour nous lorsque nous avons appelé la fonction `.deploy()`. Deux méthodes importantes ici sont [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), qui est la requête pour écrire notre contrat sur la chaîne Goerli, et [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash), qui est une requête pour lire les informations sur notre transaction à partir du hachage. Pour en savoir plus sur l'envoi de transactions, consultez [notre tutoriel sur l'envoi de transactions à l'aide de Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). ## Partie 2 : Interagir avec votre contrat intelligent {#part-2-interact-with-your-smart-contract} Maintenant que nous avons déployé avec succès un contrat intelligent sur le réseau Goerli, apprenons à interagir avec lui. - - -### Créez un fichier interact.js {#create-a-interactjs-file} +### Créer un fichier interact.js {#create-a-interactjs-file} C'est dans ce fichier que nous écrirons notre script d'interaction. Nous utiliserons la bibliothèque Ethers.js que vous avez précédemment installée dans la Partie 1. À l'intérieur du dossier `scripts/`, créez un nouveau fichier nommé `interact.js` et ajoutez le code suivant : - - ```javascript // interact.js @@ -478,19 +391,14 @@ const PRIVATE_KEY = process.env.PRIVATE_KEY const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS ``` - - - ### Mettez à jour votre fichier .env {#update-your-env-file} -Nous utiliserons de nouvelles variables d'environnement, donc nous devons les définir dans le fichier `.env` que [nous avons créé précédemment](#step-11-connect-metamask-&-alchemy-to-your-project). +Nous allons utiliser de nouvelles variables d'environnement, nous devons donc les définir dans le fichier `.env` que [nous avons créé précédemment](#step-11-connect-metamask-&-alchemy-to-your-project). -Nous devrons ajouter une définition pour notre `API_KEY` Alchemy et la `CONTRACT_ADDRESS` où votre contrat intelligent a été déployé. +Nous devrons ajouter une définition pour notre `API_KEY` Alchemy et le `CONTRACT_ADDRESS` où votre contrat intelligent a été déployé. Votre fichier `.env` devrait ressembler à ceci : - - ```bash # .env @@ -500,66 +408,50 @@ PRIVATE_KEY = "" CONTRACT_ADDRESS = "0x" ``` +### Récupérez l'ABI de votre contrat {#grab-your-contract-ABI} - - -### Obtenez votre ABI de contrat {#grab-your-contract-ABI} - -Notre [ABI de contrat (Interface Binaire d'Application)](/glossary/#abi) est l'interface pour interagir avec notre contrat intelligent. Hardhat génère automatiquement un ABI et le sauvegarde dans `HelloWorld.json`. Pour utiliser l'ABI, nous devons en extraire le contenu en ajoutant les lignes de code suivantes à notre fichier `interact.js` : - - +L'[ABI (Application Binary Interface)](/glossary/#abi) de notre contrat est l'interface pour interagir avec notre contrat intelligent. Hardhat génère automatiquement un ABI et le sauvegarde dans `HelloWorld.json`. Pour utiliser l'ABI, nous devons en extraire le contenu en ajoutant les lignes de code suivantes à notre fichier `interact.js` : ```javascript // interact.js const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.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 : ```javascript console.log(JSON.stringify(contract.abi)) ``` - -Pour voir votre ABI affiché dans la console, naviguez vers votre terminal et exécutez : - - +Pour voir votre ABI s'afficher dans la console, naviguez vers votre terminal et exécutez : ```bash npx hardhat run scripts/interact.js ``` - - - -### Créez une instance de votre contrat {#create-an-instance-of-your-contract} +### Créer une instance de votre contrat {#create-an-instance-of-your-contract} Pour interagir avec notre contrat, nous devons créer une instance de contrat dans notre code. Pour ce faire avec Ethers.js, nous devrons travailler avec trois concepts : 1. Fournisseur - un fournisseur de nœud qui vous donne un accès en lecture et écriture à la blockchain -2. Signataire - représente un compte Ethereum pouvant signer des transactions +2. Signataire - représente un compte Ethereum qui peut signer des transactions 3. Contrat - un objet Ethers.js représentant un contrat spécifique déployé sur la chaîne Nous utiliserons l'ABI de contrat de l'étape précédente pour créer notre instance du contrat : - - ```javascript // interact.js -// Provider +// Fournisseur const alchemyProvider = new ethers.providers.AlchemyProvider( (network = "goerli"), API_KEY ) -// Signer +// Signataire const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) -// Contract +// Contrat const helloWorldContract = new ethers.Contract( CONTRACT_ADDRESS, contract.abi, @@ -567,12 +459,9 @@ const helloWorldContract = new ethers.Contract( ) ``` +En savoir plus sur les Fournisseurs, les Signataires et les Contrats dans la [documentation d'ethers.js](https://docs.ethers.io/v5/). -En savoir plus sur les Fournisseurs, les Signataires, et les Contrats dans la [documentation d'ethers.js](https://docs.ethers.io/v5/). - - - -### Lisez le message d'initialisation {#read-the-init-message} +### Lire le message d'initialisation {#read-the-init-message} Vous souvenez-vous lorsque nous avons déployé notre contrat avec `initMessage = "Hello world!"` ? Nous allons maintenant lire ce message stocké dans notre contrat intelligent et l'afficher sur la console. @@ -580,8 +469,6 @@ En JavaScript, des fonctions asynchrones sont utilisées lors de l'interaction a Utilisez le code ci-dessous pour appeler la fonction `message` dans notre contrat intelligent et lire le message d'initialisation : - - ```javascript // interact.js @@ -589,32 +476,24 @@ Utilisez le code ci-dessous pour appeler la fonction `message` dans notre contra async function main() { const message = await helloWorldContract.message() - console.log("The message is: " + message) + console.log("Le message est : " + message) } main() ``` - Après avoir exécuté le fichier en utilisant `npx hardhat run scripts/interact.js` dans le terminal, nous devrions voir cette réponse : - - ``` -The message is: Hello world! +Le message est : Hello world! ``` - -Félicitations ! Vous venez de lire avec succès des données de contrat intelligent depuis la blockchain Ethereum, bravo ! - - +Félicitations ! Vous venez de lire avec succès des données de contrat intelligent depuis la blockchain Ethereum, bravo ! ### Mettre à jour le message {#update-the-message} Au lieu de simplement lire le message, nous pouvons également mettre à jour le message sauvegardé dans notre contrat intelligent en utilisant la fonction `update` ! Plutôt cool, n'est-ce pas ? -Pour mettre à jour le message, nous pouvons appeler directement la fonction `update` sur notre objet Contract : - - +Pour mettre à jour le message, nous pouvons appeler directement la fonction `update` sur notre objet de Contrat instancié : ```javascript // interact.js @@ -623,28 +502,23 @@ Pour mettre à jour le message, nous pouvons appeler directement la fonction `up async function main() { const message = await helloWorldContract.message() - console.log("The message is: " + message) + console.log("Le message est : " + message) - console.log("Updating the message...") - const tx = await helloWorldContract.update("This is the new message.") + console.log("Mise à jour du message...") + const tx = await helloWorldContract.update("Ceci est le nouveau message.") await tx.wait() } main() ``` +Notez qu'à la ligne 11, nous faisons un appel à `.wait()` sur l'objet de transaction renvoyé. Cela garantit que notre script attend que la transaction soit minée sur la blockchain avant de quitter la fonction. Si l'appel `.wait()` n'est pas inclus, le script risque de ne pas voir la valeur mise à jour du `message` dans le contrat. -Notez qu'à la ligne 11, nous faisons un appel à `.wait()` sur l'objet de transaction renvoyé. Cela garantit que notre script attend que la transaction soit exécutée sur la blockchain avant de quitter la fonction. Si l'appel `.wait()` n'est pas inclus, le script peut ne pas voir la valeur du `message` mis à jour dans le contrat. - +### Lire le nouveau message {#read-the-new-message} - -### Lisez le nouveau message {#read-the-new-message} - -Vous devriez pouvoir répéter [l'étape précédente](#read-the-init-message) pour lire la valeur du `message` mis à jour. Prenez un moment et voyez si vous pouvez apporter les modifications nécessaires pour afficher cette nouvelle valeur ! +Vous devriez pouvoir répéter l'[étape précédente](#read-the-init-message) pour lire la valeur du `message` mis à jour. Prenez un moment et voyez si vous pouvez apporter les modifications nécessaires pour afficher cette nouvelle valeur ! Si vous avez besoin d'un indice, voici à quoi devrait ressembler votre fichier `interact.js` à ce stade : - - ```javascript // interact.js @@ -654,16 +528,16 @@ const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") -// provider - Alchemy +// fournisseur - Alchemy const alchemyProvider = new ethers.providers.AlchemyProvider( (network = "goerli"), API_KEY ) -// signer - you +// signataire - vous const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) -// contract instance +// instance de contrat const helloWorldContract = new ethers.Contract( CONTRACT_ADDRESS, contract.abi, @@ -672,54 +546,46 @@ const helloWorldContract = new ethers.Contract( async function main() { const message = await helloWorldContract.message() - console.log("The message is: " + message) + console.log("Le message est : " + message) - console.log("Updating the message...") - const tx = await helloWorldContract.update("this is the new message") + console.log("Mise à jour du message...") + const tx = await helloWorldContract.update("ceci est le nouveau message") await tx.wait() const newMessage = await helloWorldContract.message() - console.log("The new message is: " + newMessage) + console.log("Le nouveau message est : " + newMessage) } main() ``` - Exécutez simplement le script et vous devriez pouvoir voir l'ancien message, le statut de la mise à jour, et le nouveau message affiché sur votre terminal ! `npx hardhat run scripts/interact.js --network goerli` - - ``` -The message is: Hello World! -Updating the message... -The new message is: This is the new message. +Le message est : Hello World! +Mise à jour du message... +Le nouveau message est : Ceci est le nouveau message. ``` +Lors de l'exécution de ce script, vous remarquerez peut-être que l'étape « Mise à jour du message... » prend du temps à charger avant que le nouveau message ne s'affiche. Cela est dû au processus de minage ; si vous êtes curieux de suivre les transactions pendant qu'elles sont en cours de minage, visitez la [mempool d'Alchemy](https://dashboard.alchemyapi.io/mempool) pour voir le statut d'une transaction. Si la transaction est abandonnée, il est également utile de consulter [l'Etherscan de Goerli](https://goerli.etherscan.io) et de rechercher le hachage de votre transaction. -Lors de l'exécution de ce script, vous remarquerez peut-être que l'étape `Mise à jour du message...` prend du temps à charger avant que le nouveau message ne s'affiche. Cela est dû au processus de minage ; si vous êtes curieux de suivre les transactions pendant qu'elles sont en cours de minage, visitez la [mempool d'Alchemy](https://dashboard.alchemyapi.io/mempool) pour voir le statut d'une transaction. Si la transaction est abandonnée, il est également utile de consulter [Goerli Etherscan](https://goerli.etherscan.io) et de rechercher votre hash de transaction. - - - -## Partie 3 : Publier votre Contrat Intelligent sur Etherscan {#part-3-publish-your-smart-contract-to-etherscan} +## Partie 3 : Publier votre contrat intelligent sur Etherscan {#part-3-publish-your-smart-contract-to-etherscan} Vous avez fait tout le travail difficile pour donner vie à votre contrat intelligent ; il est maintenant temps de le partager avec le monde ! -En vérifiant votre contrat intelligent sur Etherscan, tout le monde peut voir votre code source et interagir avec votre contrat intelligent. Commençons ! - - +En vérifiant votre contrat intelligent sur Etherscan, n'importe qui peut voir votre code source et interagir avec votre contrat intelligent. Commençons ! -### Étape 1 : Générez une clé API sur votre compte Etherscan {#step-1-generate-an-api-key-on-your-etherscan-account} +### Étape 1 : Générer une clé API sur votre compte Etherscan {#step-1-generate-an-api-key-on-your-etherscan-account} Une clé API Etherscan est nécessaire pour vérifier que vous possédez le contrat intelligent que vous essayez de publier. -Si vous n'avez pas encore de compte Etherscan, [inscrivez-vous](https://etherscan.io/register). +Si vous n'avez pas encore de compte Etherscan, [inscrivez-vous pour un compte](https://etherscan.io/register). -Une fois connecté, trouvez votre nom d'utilisateur dans la barre de navigation, passez votre souris dessus et sélectionnez le bouton **Mon profil**. +Une fois connecté, trouvez votre nom d'utilisateur dans la barre de navigation, survolez-le et sélectionnez le bouton **Mon profil**. -Sur votre page de profil, vous devriez voir une barre de navigation latérale. Dans cette barre de navigation, sélectionnez **Clés API**. Ensuite, appuyez sur le bouton « Ajouter » pour créer une nouvelle clé API, nommez votre application **hello-world** et appuyez sur le bouton **Créer une nouvelle clé API**. +Sur votre page de profil, vous devriez voir une barre de navigation latérale. Depuis la barre de navigation latérale, sélectionnez **Clés API**. Ensuite, appuyez sur le bouton « Ajouter » pour créer une nouvelle clé API, nommez votre application **hello-world** et appuyez sur le bouton **Créer une nouvelle clé API**. Votre nouvelle clé API devrait apparaître dans le tableau des clés API. Copiez la clé API dans votre presse-papiers. @@ -727,8 +593,6 @@ Ensuite, nous devons ajouter la clé API d'Etherscan à notre fichier `.env`. Après l'avoir ajoutée, votre fichier `.env` devrait ressembler à ceci : - - ```javascript API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" PUBLIC_KEY = "your-public-account-address" @@ -737,27 +601,17 @@ CONTRACT_ADDRESS = "your-contract-address" ETHERSCAN_API_KEY = "your-etherscan-key" ``` +### Contrats intelligents déployés par Hardhat {#hardhat-deployed-smart-contracts} - - -### Contrats intelligents déployés avec Hardhat {#hardhat-deployed-smart-contracts} - - - -#### Installez hardhat-etherscan {#install-hardhat-etherscan} +#### Installer hardhat-etherscan {#install-hardhat-etherscan} Publier votre contrat sur Etherscan à l'aide de Hardhat est simple. Vous devrez d'abord installer le plugin `hardhat-etherscan` pour commencer. `hardhat-etherscan` vérifiera automatiquement le code source et l'ABI du contrat intelligent sur Etherscan. Pour l'ajouter, dans le répertoire `hello-world`, exécutez : - - ```text npm install --save-dev @nomiclabs/hardhat-etherscan ``` - -Une fois installé, incluez la déclaration suivante en haut de votre fichier `hardhat.config.js`, et ajoutez les options de configuration d'Etherscan : - - +Une fois installé, incluez la déclaration suivante en haut de votre `hardhat.config.js`, et ajoutez les options de configuration d'Etherscan : ```javascript // hardhat.config.js @@ -779,129 +633,100 @@ module.exports = { }, }, etherscan: { - // Your API key for Etherscan - // Obtain one at https://etherscan.io/ + // Votre clé API pour Etherscan + // Obtenez-en une sur https://etherscan.io/ apiKey: ETHERSCAN_API_KEY, }, } ``` - - - #### Vérifiez votre contrat intelligent sur Etherscan {#verify-your-smart-contract-on-etherscan} Assurez-vous que tous les fichiers sont sauvegardés et que toutes les variables `.env` sont correctement configurées. -Exécutez la tâche `verify`, en envoyant l'adresse du contrat et le réseau sur lequel il est déployé : - - +Exécutez la tâche `verify`, en passant l'adresse du contrat et le réseau sur lequel il est déployé : ```text npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!' ``` - -Assurez-vous que `DEPLOYED_CONTRACT_ADDRESS` est l'adresse de votre contrat intelligent déployé sur le réseau de test Goerli. De plus, le dernier argument (`Hello World!`) doit être la même valeur de chaîne utilisée lors de [l'étape de déploiement de la partie 1](#write-our-deploy-script). +Assurez-vous que `DEPLOYED_CONTRACT_ADDRESS` est l'adresse de votre contrat intelligent déployé sur le réseau de test Goerli. De plus, l'argument final (`'Hello World!'`) doit être la même valeur de chaîne que celle utilisée [lors de l'étape de déploiement de la partie 1](#write-our-deploy-script). Si tout se passe bien, vous verrez le message suivant dans votre terminal : - - ```text -Successfully submitted source code for contract +Code source du contrat soumis avec succès contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address -for verification on Etherscan. Waiting for verification result... +pour vérification sur Etherscan. En attente du résultat de la vérification... -Successfully verified contract HelloWorld on Etherscan. +Contrat HelloWorld vérifié avec succès sur Etherscan. https://goerli.etherscan.io/address/#contracts ``` +Félicitations ! Votre code de contrat intelligent est sur Etherscan ! -Félicitations ! Votre code de contrat intelligent est sur Etherscan ! - +### Consultez votre contrat intelligent sur Etherscan ! {#check-out-your-smart-contract-on-etherscan} +Lorsque vous naviguez vers le lien fourni dans votre terminal, vous devriez pouvoir voir votre code de contrat intelligent et l'ABI publiés sur Etherscan ! -### Votre code de contrat intelligent est sur Etherscan ! {#check-out-your-smart-contract-on-etherscan} +**Wahooo - vous l'avez fait, champion ! Désormais, n'importe qui peut appeler ou écrire dans votre contrat intelligent ! Nous sommes impatients de voir ce que vous construirez ensuite !** -Lorsque vous naviguez vers le lien fourni dans votre terminal, vous devriez pouvoir voir votre code de contrat intelligent et ABI publié sur Etherscan ! - -**Wahooo - vous l'avez fait, champion ! Désormais, n'importe qui peut appeler ou écrire à votre contrat intelligent ! Nous sommes impatients de voir ce que vous construirez ensuite !** - - - -## Partie 4 - Intégration de votre contrat intelligent avec l'interface utilisateur {#part-4-integrating-your-smart-contract-with-the-frontend} +## Partie 4 - Intégration de votre contrat intelligent avec le frontend {#part-4-integrating-your-smart-contract-with-the-frontend} À la fin de ce tutoriel, vous saurez comment : - Connecter un portefeuille MetaMask à votre dapp -- Lire les données de votre contrat intelligent en utilisant l'API [Web3 d'Alchemy](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) +- Lire des données de votre contrat intelligent en utilisant l'API [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) - Signer des transactions Ethereum en utilisant MetaMask -Pour cette DApp, nous utiliserons [React](https://reactjs.org/) comme cadre d'interface utilisateur ; cependant, il est important de noter que nous ne passerons pas beaucoup de temps à décomposer ses fondamentaux, car nous nous concentrerons principalement sur l'ajout de la fonctionnalité Web3 à notre projet. - -En prérequis, vous devriez avoir une compréhension de niveau débutant de React. Sinon, nous recommandons de compléter le tutoriel officiel [Introduction à React](https://reactjs.org/tutorial/tutorial.html). - +Pour cette dapp, nous utiliserons [React](https://react.dev/) comme notre framework frontend ; cependant, il est important de noter que nous ne passerons pas beaucoup de temps à décomposer ses fondamentaux, car nous nous concentrerons principalement sur l'intégration de la fonctionnalité Web3 à notre projet. +En prérequis, vous devriez avoir une compréhension de niveau débutant de React. Sinon, nous vous recommandons de suivre le [tutoriel officiel d'introduction à React](https://react.dev/learn). ### Cloner les fichiers de démarrage {#clone-the-starter-files} -Tout d'abord, allez au [dépôt GitHub hello-world-part-four](https://github.com/alchemyplatform/hello-world-part-four-tutorial) pour obtenir les fichiers de départ de ce projet et clonez ce dépôt sur votre machine locale. +Tout d'abord, rendez-vous sur le [dépôt GitHub hello-world-part-four](https://github.com/alchemyplatform/hello-world-part-four-tutorial) pour obtenir les fichiers de démarrage de ce projet et clonez ce dépôt sur votre machine locale. Ouvrez le dépôt cloné localement. Remarquez qu'il contient deux dossiers : `starter-files` et `completed`. -- `starter-files`-**nous travaillerons dans ce répertoire**, nous connecterons l'UI à votre portefeuille Ethereum et le contrat intelligent que nous avons publié sur Etherscan lors de la [Partie 3](#part-3). -- `completed` contient l'ensemble du tutoriel terminé et ne doit être utilisé que comme référence si vous êtes bloqué. +- `starter-files` - **nous travaillerons dans ce répertoire**, nous connecterons l'interface utilisateur à votre portefeuille Ethereum et au contrat intelligent que nous avons publié sur Etherscan dans la [Partie 3](#part-3). +- `completed` contient le tutoriel complet et ne doit être utilisé que comme référence si vous êtes bloqué. Ensuite, ouvrez votre copie de `starter-files` avec votre éditeur de code préféré, puis naviguez dans le dossier `src`. -Tout le code que nous allons écrire restera dans le dossier `src`. Nous modifierons le composant `HelloWorld.js` et les fichiers JavaScript `util/interact.js` pour donner à notre projet des fonctionnalités Web3. - - +Tout le code que nous allons écrire se trouvera dans le dossier `src`. Nous modifierons le composant `HelloWorld.js` et les fichiers JavaScript `util/interact.js` pour donner à notre projet des fonctionnalités Web3. -### Consultez les fichiers de départ {#check-out-the-starter-files} +### Consultez les fichiers de démarrage {#check-out-the-starter-files} Avant de commencer à coder, explorons ce qui nous est fourni dans les fichiers de départ. - - -#### 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. -Pour faire fonctionner le projet, accédez au répertoire racine du dossier `starter-files` et exécutez `npm install` dans votre terminal pour installer les dépendances du projet : - - +Pour lancer le projet, naviguez vers le répertoire racine du dossier `starter-files`, et exécutez `npm install` dans votre terminal pour installer les dépendances du projet : ```bash cd starter-files npm install ``` - Une fois l'installation terminée, exécutez `npm start` dans votre terminal : - - ```bash npm start ``` - -Cela devrait ouvrir [http://localhost:3000/](http://localhost:3000/) dans votre navigateur, où vous verrez l'interface utilisateur pour notre projet. Elle devrait se composer d'un champ \(a un endroit pour mettre à jour le message stocké dans votre contrat intelligent\), d'un bouton « Connecter le portefeuille » et d'un bouton « Mettre à jour ». +Cela devrait ouvrir [http://localhost:3000/](http://localhost:3000/) dans votre navigateur, où vous verrez le frontend de notre projet. Il devrait se composer d'un champ (un endroit pour mettre à jour le message stocké dans votre contrat intelligent), d'un bouton « Connecter le portefeuille » et d'un bouton « Mettre à jour ». Si vous essayez de cliquer sur l'un ou l'autre des boutons, vous remarquerez qu'ils ne fonctionnent pas - c'est parce que nous devons encore programmer leur fonctionnalité. - - #### Le composant `HelloWorld.js` {#the-helloworld-js-component} Retournons dans le dossier `src` de notre éditeur et ouvrons le fichier `HelloWorld.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, vous remarquerez que nous avons plusieurs instructions d'importation nécessaires pour faire fonctionner notre projet, notamment la bibliothèque React, les accroches useEffect et useState, certains éléments de `./util/interact.js` (nous les décrirons plus en détail bientôt !), et le logo Alchemy. - - +En haut de ce fichier, vous remarquerez que nous avons plusieurs instructions d'importation nécessaires pour faire fonctionner notre projet, y compris la bibliothèque React, les hooks useEffect et useState, certains éléments de `./util/interact.js` (nous les décrirons plus en détail bientôt !), et le logo d'Alchemy. ```javascript // HelloWorld.js @@ -919,136 +744,123 @@ import { import alchemylogo from "./alchemylogo.svg" ``` - Ensuite, nous avons nos variables d'état que nous mettrons à jour après des événements spécifiques. - - ```javascript // HelloWorld.js -//State variables +//Variables d'état const [walletAddress, setWallet] = useState("") const [status, setStatus] = useState("") -const [message, setMessage] = useState("No connection to the network.") +const [message, setMessage] = useState("Pas de connexion au réseau.") const [newMessage, setNewMessage] = useState("") ``` - 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ères qui stocke un message utile guidant l'utilisateur sur la façon d'interagir avec la DApp +- `walletAddress` : une chaîne qui stocke l'adresse du portefeuille de l'utilisateur +- `status` - une chaîne de caractères qui stocke un message utile guidant l'utilisateur sur la façon d'interagir avec la dapp - `message` - une chaîne qui stocke le message actuel dans le contrat intelligent - `newMessage` - une chaîne qui stocke le nouveau message qui sera écrit dans le contrat intelligent -Après les variables d'état, vous verrez cinq fonctions non implémentées : `useEffect` ,`addSmartContractListener`, `addWalletListener` , `connectWalletPressed`, et `onUpdatePressed`. Nous expliquerons ce qu'elles font ci-dessous : - - +Après les variables d'état, vous verrez cinq fonctions non implémentées : `useEffect`, `addSmartContractListener`, `addWalletListener`, `connectWalletPressed`, et `onUpdatePressed`. Nous expliquerons ce qu'elles font ci-dessous : ```javascript // HelloWorld.js -//called only once +//appelé une seule fois useEffect(async () => { - //TODO: implement + //TODO: implémenter }, []) function addSmartContractListener() { - //TODO: implement + //TODO: implémenter } function addWalletListener() { - //TODO: implement + //TODO: implémenter } const connectWalletPressed = async () => { - //TODO: implement + //TODO: implémenter } const onUpdatePressed = async () => { - //TODO: implement + //TODO: implémenter } ``` - -- [`useEffect`](https://reactjs.org/docs/hooks-effect.html) - c'est une accroche React qui est appelé après que votre composant est rendu. Comme il a un tableau vide `[]` passé en prop (voir ligne 4), il ne sera appelé qu'au _premier_ rendu du composant. Ici, nous chargerons le message actuel stocké dans notre contrat intelligent, appellerons nos écouteurs de contrat intelligent et de portefeuille, et mettrons à jour notre interface pour refléter si un portefeuille est déjà connecté. -- `addSmartContractListener` - cette fonction configure un écouteur qui surveillera l'événement `UpdatedMessages` de notre contrat HelloWorld et mettra à jour notre interface lorsque le message sera modifié dans notre contrat intelligent. +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) - c'est un hook React qui est appelé après que votre composant est rendu. Comme il a un tableau vide `[]` passé en paramètre (voir ligne 4), il ne sera appelé qu'au _premier_ rendu du composant. Ici, nous chargerons le message actuel stocké dans notre contrat intelligent, appellerons nos écouteurs de contrat intelligent et de portefeuille, et mettrons à jour notre interface pour refléter si un portefeuille est déjà connecté. +- `addSmartContractListener` - cette fonction configure un écouteur qui surveillera l'événement `UpdatedMessages` de notre contrat HelloWorld et mettra à jour notre interface utilisateur lorsque le message sera modifié dans notre contrat intelligent. - `addWalletListener` - cette fonction configure un écouteur qui détecte les changements dans l'état du portefeuille MetaMask de l'utilisateur, par exemple lorsque l'utilisateur déconnecte son portefeuille ou change d'adresse. -- `connectWalletPressed`- cette fonction sera appelée pour connecter le portefeuille MetaMask de l'utilisateur à notre DApp. +- `connectWalletPressed` - cette fonction sera appelée pour connecter le portefeuille MetaMask de l'utilisateur à notre dapp. - `onUpdatePressed` - cette fonction sera appelée lorsque l'utilisateur souhaite mettre à jour le message stocké dans le contrat intelligent. Vers la fin de ce fichier, nous avons l'interface utilisateur de notre composant. - - ```javascript // HelloWorld.js -//the UI of our component +//l'interface utilisateur de notre composant return (
-

Current Message:

+

Message actuel :

{message}

-

New Message:

+

Nouveau message :

setNewMessage(e.target.value)} value={newMessage} />

{status}

-
-
+ + + ) ``` +Si vous analysez attentivement ce code, vous remarquerez où nous utilisons nos différentes variables d'état dans notre interface utilisateur : -Si vous examinez attentivement ce code, vous remarquerez où nous utilisons nos différentes variables d'état dans notre interface : - -- Aux lignes 6-12, si le portefeuille de l'utilisateur est connecté \(c'est-à-dire si `walletAddress.length > 0`\), nous affichons une version tronquée de `walletAddress` de l'utilisateur dans le bouton avec l'ID « walletButton » ; sinon, il indique simplement « Connecter le portefeuille. » -- À la ligne 17, nous affichons le message actuel stocké dans le contrat intelligent, qui est inclus dans la chaîne de caractères `message`. -- Aux lignes 23-26, nous utilisons un [composant contrôlé](https://reactjs.org/docs/forms.html#controlled-components) pour mettre à jour notre variable d'état `newMessage` lorsque l'entrée dans le champ de texte change. +- Aux lignes 6-12, si le portefeuille de l'utilisateur est connecté (c'est-à-dire `walletAddress.length > 0`), nous affichons une version tronquée de l'`walletAddress` de l'utilisateur dans le bouton avec l'ID "walletButton"; sinon, il indique simplement "Connecter le portefeuille". +- À la ligne 17, nous affichons le message actuel stocké dans le contrat intelligent, qui est capturé dans la chaîne `message`. +- Aux lignes 23-26, nous utilisons un [composant contrôlé](https://legacy.reactjs.org/docs/forms.html#controlled-components) pour mettre à jour notre variable d'état `newMessage` lorsque l'entrée dans le champ de texte change. En plus de nos variables d'état, vous verrez également que les fonctions `connectWalletPressed` et `onUpdatePressed` sont appelées lorsque les boutons avec les ID `publishButton` et `walletButton` sont cliqués respectivement. -Enfin, regardons où ce composant `HelloWorld.js` est ajouté. - -Si vous allez au fichier `App.js`, qui est le composant principal dans React qui sert de conteneur pour tous les autres composants, vous verrez que notre composant `HelloWorld.js` est injecté à la ligne 7. - -En dernier lieu, vérifions un autre fichier fourni pour vous, le fichier `interact.js`. +Enfin, voyons où ce composant `HelloWorld.js` est ajouté. +Si vous allez dans le fichier `App.js`, qui est le composant principal de React agissant comme un conteneur pour tous les autres composants, vous verrez que notre composant `HelloWorld.js` est injecté à la ligne 7. +Enfin et surtout, examinons un autre fichier qui vous est fourni, le fichier `interact.js`. #### Le fichier `interact.js` {#the-interact-js-file} -Pour respecter le paradigme [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) , nous voulons un fichier séparé qui contient toutes nos fonctions pour gérer la logique, les données, et les règles de notre DApp, puis nous pourrons passer ces fonctions à notre interface \(notre composant `HelloWorld.js` \). +Parce que nous voulons adhérer au paradigme [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), nous voudrons un fichier séparé qui contient toutes nos fonctions pour gérer la logique, les données et les règles de notre dapp, puis pouvoir exporter ces fonctions vers notre frontend (notre composant `HelloWorld.js`). 👆🏽C'est exactement le but de notre fichier `interact.js` ! Naviguez vers le dossier `util` dans votre répertoire `src`, et vous remarquerez que nous avons inclus un fichier appelé `interact.js` qui contiendra toutes nos fonctions et variables d'interaction avec le contrat intelligent et le portefeuille. - - ```javascript // interact.js @@ -1063,71 +875,55 @@ const getCurrentWalletConnected = async () => {} export const updateMessage = async (message) => {} ``` - Vous remarquerez en haut du fichier que nous avons commenté l'objet `helloWorldContract`. Plus tard dans ce tutoriel, nous décommenterons cet objet et instancierons notre contrat intelligent dans cette variable, que nous exporterons ensuite dans notre composant `HelloWorld.js`. Les quatre fonctions non implémentées après notre objet `helloWorldContract` font ce qui suit : -- `loadCurrentMessage` - cette fonction gère la logique du chargement du message actuel stocké dans le contrat intelligent. Elle effectuera un appel en _lecture_ au contrat intelligent Hello World en utilisant [l'API Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3). -- `connectWallet` - cette fonction connectera le portefeuille MetaMask de l'utilisateur à notre DApp. -- `getCurrentWalletConnected` - cette fonction vérifiera si un compte Ethereum est déjà connecté à notre DApp lors du chargement de la page et mettra à jour notre interface en conséquence. +- `loadCurrentMessage` - cette fonction gère la logique de chargement du message actuel stocké dans le contrat intelligent. Elle effectuera un appel en _lecture_ au contrat intelligent Hello World en utilisant l'[API Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3). +- `connectWallet` - cette fonction connectera le MetaMask de l'utilisateur à notre dapp. +- `getCurrentWalletConnected` - cette fonction vérifiera si un compte Ethereum est déjà connecté à notre dapp lors du chargement de la page et mettra à jour notre interface utilisateur en conséquence. - `updateMessage` - cette fonction mettra à jour le message stocké dans le contrat intelligent. Elle effectuera un appel en _écriture_ au contrat intelligent Hello World, donc le portefeuille MetaMask de l'utilisateur devra signer une transaction Ethereum pour mettre à jour le message. Maintenant que nous comprenons avec quoi nous travaillons, voyons comment lire notre contrat intelligent ! - - ### Étape 3 : Lire à partir de votre contrat intelligent {#step-3-read-from-your-smart-contract} -Pour lire à partir de votre contrat intelligent, vous devrez configurer correctement : +Pour lire à partir de votre contrat intelligent, vous devrez configurer avec succès : - Une connexion API à la chaîne Ethereum - Une instance chargée de votre contrat intelligent -- Une fonction pour appeler votre fonction de contrat intelligent -- Un écouteur pour surveiller les mises à jour lorsque les données que vous lisez du contrat intelligent changent +- Une fonction pour appeler la fonction de votre contrat intelligent +- Un écouteur pour surveiller les mises à jour lorsque les données que vous lisez à partir du contrat intelligent changent Cela peut sembler beaucoup d'étapes, mais ne vous inquiétez pas ! Nous allons vous guider étape par étape ! :\) - - #### Établir une connexion API à la chaîne Ethereum {#establish-an-api-connection-to-the-ethereum-chain} -Rappelez-vous, dans la Partie 2 de ce tutoriel, comment nous avons utilisé notre clé [Web3 Alchemy pour lire à partir de notre contrat intelligent](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library) ? Vous aurez également besoin d'une clé Web3 Alchemy dans votre DApp pour lire à partir de la chaîne. - -Si vous ne l'avez pas déjà fait, installez d'abord [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) en naviguant vers le répertoire racine de vos `starter-files` et en exécutant la commande suivante dans votre terminal : - +Alors, souvenez-vous comment, dans la partie 2 de ce tutoriel, nous avons utilisé notre [clé Alchemy Web3 pour lire notre contrat intelligent](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library) ? Vous aurez également besoin d'une clé Alchemy Web3 dans votre dapp pour lire à partir de la chaîne. +Si vous ne l'avez pas déjà, installez d'abord [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) en naviguant vers le répertoire racine de vos `starter-files` et en exécutant la commande suivante dans votre terminal : ```text npm install @alch/alchemy-web3 ``` +[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 ! -[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 ! - -Ensuite, installez le paquet [dotenv](https://www.npmjs.com/package/dotenv) dans le répertoire de votre projet, afin d'avoir un endroit sécurisé pour stocker notre clé API après l'avoir récupérée. - - +Ensuite, installez le paquet [dotenv](https://www.npmjs.com/package/dotenv) dans le répertoire de votre projet, afin que nous ayons un endroit sécurisé pour stocker notre clé API après l'avoir récupérée. ```text npm install dotenv --save ``` +Pour notre dapp, **nous utiliserons notre clé API Websockets** au lieu de notre clé API HTTP, car cela nous permettra de mettre en place un écouteur qui détecte lorsque le message stocké dans le contrat intelligent change. -Pour notre dapp, **nous utiliserons notre clé API Websockets** au lieu de notre clé API HTTP, car elle nous permettra d'écouteur pour détecter lorsque le message stocké dans le contrat intelligent change. - -Une fois que vous avez votre clé API, créez un fichier `.env` dans votre répertoire racine et ajoutez-y votre URL Websockets Alchemy. Ensuite, votre fichier `.env` devrait ressembler à cela : - - +Une fois que vous avez votre clé API, créez un fichier `.env` dans votre répertoire racine et ajoutez-y votre URL Websockets Alchemy. Ensuite, votre fichier `.env` devrait ressembler à ceci : ```javascript REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/ ``` - -Maintenant, nous sommes prêts à configurer notre point de terminaison Web3 Alchemy dans notre DApp ! Retournons à notre `interact.js`, qui est niché à l'intérieur de notre dossier `util` et ajoutons le code suivant en haut du fichier : - - +Maintenant, nous sommes prêts à configurer notre point de terminaison Alchemy Web3 dans notre dapp ! Retournons à notre `interact.js`, qui est imbriqué dans notre dossier `util`, et ajoutons le code suivant en haut du fichier : ```javascript // interact.js @@ -1140,30 +936,23 @@ const web3 = createAlchemyWeb3(alchemyKey) //export const helloWorldContract; ``` +Ci-dessus, nous avons d'abord importé la clé Alchemy depuis notre fichier `.env`, puis nous avons passé notre `alchemyKey` à `createAlchemyWeb3` pour établir notre point de terminaison Alchemy Web3. -Ci-dessus, nous avons d'abord importé la clé Alchemy de notre fichier `.env`, puis nous avons passé notre `alchemyKey` à `createAlchemyWeb3` pour établir notre point de terminaison Web3 Alchemy. - -Avec ce point de terminaison prêt, il est temps de charger notre contrat intelligent Hello World ! - - +Avec ce point de terminaison prêt, il est temps de charger notre contrat intelligent ! #### Chargement de votre contrat intelligent Hello World {#loading-your-hello-world-smart-contract} -Pour charger votre contrat intelligent Hello World, vous aurez besoin de son adresse de contrat et de son ABI, tous deux disponibles sur Etherscan si vous avez terminé [la Partie 3 de ce tutoriel.](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan) - - +Pour charger votre contrat intelligent Hello World, vous aurez besoin de son adresse de contrat et de son ABI, qui peuvent tous deux être trouvés sur Etherscan si vous avez terminé la [Partie 3 de ce tutoriel](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan). -#### Comment obtenir votre ABI de contrat depuis Etherscan {#how-to-get-your-contract-abi-from-etherscan} +#### Comment obtenir l'ABI de votre contrat depuis Etherscan {#how-to-get-your-contract-abi-from-etherscan} -Si vous avez ignoré la Partie 3 de ce tutoriel, vous pouvez utiliser le contrat HelloWorld avec l'adresse [0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). Son ABI se trouve [ici](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). +Si vous avez sauté la partie 3 de ce tutoriel, vous pouvez utiliser le contrat HelloWorld avec l'adresse [0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). Son ABI peut être trouvé [ici](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). -Un ABI de contrat est nécessaire pour spécifier quelle fonction un contrat invoquera et pour garantir que la fonction renverra des données dans le format que vous attendez. Une fois que nous avons copié notre ABI de contrat, sauvegardons-le en tant que fichier JSON appelé `contract-abi.json` dans votre répertoire `src`. +L'ABI d'un contrat est nécessaire pour spécifier quelle fonction un contrat invoquera, ainsi que pour s'assurer que la fonction renverra des données dans le format que vous attendez. Une fois que nous avons copié l'ABI de notre contrat, sauvegardons-le en tant que fichier JSON appelé `contract-abi.json` dans votre répertoire `src`. Votre contract-abi.json doit être stocké dans votre dossier src. -Armé de notre adresse de contrat, de notre ABI, et de notre point de terminaison Web3 Alchemy, nous pouvons utiliser [la méthode de contrat](https://docs.web3js.org/api/web3-eth-contract/class/Contract) pour charger une instance de notre contrat intelligent. Importez votre ABI de contrat dans le fichier `interact.js` et ajoutez votre adresse de contrat. - - +Armés de l'adresse de notre contrat, de l'ABI et du point de terminaison Alchemy Web3, nous pouvons utiliser la [méthode de contrat](https://docs.web3js.org/api/web3-eth-contract/class/Contract) pour charger une instance de notre contrat intelligent. Importez l'ABI de votre contrat dans le fichier `interact.js` et ajoutez l'adresse de votre contrat. ```javascript // interact.js @@ -1172,11 +961,8 @@ const contractABI = require("../contract-abi.json") const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" ``` - Nous pouvons maintenant enfin décommenter notre variable `helloWorldContract` et charger le contrat intelligent en utilisant notre point de terminaison AlchemyWeb3 : - - ```javascript // interact.js export const helloWorldContract = new web3.eth.Contract( @@ -1185,11 +971,8 @@ export const helloWorldContract = new web3.eth.Contract( ) ``` - Pour récapituler, les 12 premières lignes de votre `interact.js` devraient maintenant ressembler à ceci : - - ```javascript // interact.js @@ -1207,19 +990,15 @@ export const helloWorldContract = new web3.eth.Contract( ) ``` +Maintenant que notre contrat est chargé, nous pouvons implémenter notre fonction `loadCurrentMessage` ! -Maintenant que nous avons notre contrat chargé, nous pouvons implémenter notre fonction `loadCurrentMessage` ! - - +#### Implémentation de `loadCurrentMessage` dans votre fichier `interact.js` {#implementing-loadCurrentMessage-in-your-interact-js-file} -#### Implémenter `loadCurrentMessage` dans votre fichier `interact.js` {#implementing-loadCurrentMessage-in-your-interact-js-file} - -Cette fonction est très simple. Nous allons effectuer un simple appel web3 asynchrone pour lire notre contrat. Notre fonction renverra le message stocké dans le contrat intelligent : +Cette fonction est très simple. Nous allons faire un simple appel web3 asynchrone pour lire notre contrat. Notre fonction renverra le message stocké dans le contrat intelligent : Mettez à jour la fonction `loadCurrentMessage` dans votre fichier `interact.js` comme suit : - ```javascript // interact.js @@ -1229,70 +1008,60 @@ export const loadCurrentMessage = async () => { } ``` - Puisque nous voulons afficher ce contrat intelligent dans notre interface utilisateur, mettons à jour la fonction `useEffect` dans notre composant `HelloWorld.js` comme suit : - - ```javascript // HelloWorld.js -//called Orly once +//appelé une seule fois useEffect(async () => { const message = await loadCurrentMessage() - sertissage(message) + setMessage(message) }, []) ``` +Notez que nous voulons que notre `loadCurrentMessage` ne soit appelé qu'une seule fois lors du premier rendu du composant. Nous allons bientôt implémenter `addSmartContractListener` pour mettre à jour automatiquement l'interface utilisateur après que le message dans le contrat intelligent a changé. -Notez que nous voulons que notre `loadCurrentMessage` soit appelé une seule fois lors du premier rendu du composant. Nous allons bientôt implémenter `addSmartContractListener` pour mettre à jour automatiquement l'interface utilisateur après la modification du message dans le contrat intelligent. - -Avant de plonger dans notre système d'écoute, voyons ce que nous avons jusqu'à présent ! Sauvegardez vos fichiers `HelloWorld.js` et `interact.js`, puis allez sur [http://localhost:3000/](http://localhost:3000/) - -Vous remarquerez que le message actuel ne dit plus « Pas de connexion au réseau. » Au lieu de cela, il reflète le message stocké dans le contrat intelligent. C'est fou ! - +Avant de nous plonger dans notre écouteur, voyons ce que nous avons jusqu'à présent ! Enregistrez vos fichiers `HelloWorld.js` et `interact.js`, puis allez sur [http://localhost:3000/](http://localhost:3000/) +Vous remarquerez que le message actuel ne dit plus « Pas de connexion au réseau ». Au lieu de cela, il reflète le message stocké dans le contrat intelligent. Génial ! #### Votre interface utilisateur devrait maintenant refléter le message stocké dans le contrat intelligent {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} -Maintenant, parlons de cet écouteur... - - - -#### Mettre en œuvre `addSmartContractListener` {#implement-addsmartcontractlistener} - -Si vous repensez au fichier `HelloWorld.sol` que nous avons écrit dans [la première partie de cette série de tutoriels](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract), vous vous souviendrez qu'il y a un événement de contrat intelligent appelé `UpdatedMessages` qui est émis après que la fonction `update` de notre contrat intelligent soit invoquée \(voir les lignes 9 et 27\) : +Maintenant, en parlant de cet écouteur... +#### Implémenter `addSmartContractListener` {#implement-addsmartcontractlistener} +Si vous repensez au fichier `HelloWorld.sol` que nous avons écrit dans la [Partie 1 de cette série de tutoriels](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract), vous vous souviendrez qu'il y a un événement de contrat intelligent appelé `UpdatedMessages` qui est émis après l'invocation de la fonction `update` de notre contrat intelligent \(voir lignes 9 et 27\) : ```javascript // HelloWorld.sol -// Specifies the version of Solidity, using semantic versioning. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// Spécifie la version de Solidity, en utilisant le versionnage sémantique. +// En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity ^0.7.3; -// Defines a contract named `HelloWorld`. -// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// Définit un contrat nommé `HelloWorld`. +// Un contrat est une collection de fonctions et de données (son état). Une fois déployé, un contrat réside à une adresse spécifique sur la blockchain Ethereum. En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - //Emitted when update function is called - //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen. + //Émis lorsque la fonction de mise à jour est appelée + //Les événements de contrat intelligent sont un moyen pour votre contrat de communiquer que quelque chose s'est passé sur la blockchain à votre interface d'application, qui peut « écouter » certains événements et prendre des mesures lorsqu'ils se produisent. event UpdatedMessages(string oldStr, string newStr); - // Declares a state variable `message` of type `string`. - // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. + // Déclare une variable d'état `message` de type `string`. + // Les variables d'état sont des variables dont les valeurs sont stockées en permanence dans le stockage du contrat. Le mot-clé `public` rend les variables accessibles depuis l'extérieur d'un contrat et crée une fonction que d'autres contrats ou clients peuvent appeler pour accéder à la valeur. string public message; - // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. - // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + // Semblable à de nombreux langages orientés objet basés sur les classes, un constructeur est une fonction spéciale qui n'est exécutée qu'à la création du contrat. + // Les constructeurs sont utilisés pour initialiser les données du contrat. En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) { - // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). + // Accepte un argument de chaîne `initMessage` et définit la valeur dans la variable de stockage `message` du contrat). message = initMessage; } - // A public function that accepts a string argument and updates the `message` storage variable. + // Une fonction publique qui accepte un argument de chaîne et met à jour la variable de stockage `message`. function update(string memory newMessage) public { string memory oldMsg = message; message = newMessage; @@ -1301,14 +1070,11 @@ contract HelloWorld { } ``` - -Les événements de contrat intelligent sont un moyen pour votre contrat d'indiquer qu'un événement s'est produit (c'est-à-dire qu'il y a eu un _événement_) sur la blockchain à votre application front-end, qui peut « écouter » des événements spécifiques et agir lorsqu'ils se produisent. +Les événements de contrat intelligent sont un moyen pour votre contrat de communiquer qu'un événement s'est produit (c'est-à-dire qu'il y a eu un _événement_) sur la blockchain à votre application frontale, qui peut être à l'« écoute » d'événements spécifiques et prendre des mesures lorsqu'ils se produisent. La fonction `addSmartContractListener` va spécifiquement écouter l'événement `UpdatedMessages` de notre contrat intelligent Hello World et mettre à jour notre interface utilisateur pour afficher le nouveau message. -Modifiez `addSmartContractListener` de la manière suivante : - - +Modifiez `addSmartContractListener` comme suit : ```javascript // HelloWorld.js @@ -1320,21 +1086,18 @@ function addSmartContractListener() { } else { setMessage(data.returnValues[1]) setNewMessage("") - setStatus("🎉 Your message has been updated!") + setStatus("🎉 Votre message a été mis à jour !") } }) } ``` - -Décortiquons ce qui se passe lorsque l'écouteur détecte un événement : +Analysons ce qui se passe lorsque l'écouteur détecte un événement : - Si une erreur se produit lorsque l'événement est émis, elle sera reflétée dans l'interface utilisateur via notre variable d'état `status`. -- Sinon, nous utiliserons l'objet `data` renvoyé. Le `data.returnValues` est un tableau indexé à zéro où le premier élément du tableau stocke le message précédent et le deuxième élément stocke le message mis à jour. En somme, lors d'un événement réussi, nous définirons notre chaîne de `message` sur le message mis à jour, effacerons la chaîne `newMessage` et mettrons à jour notre variable d'état `status` pour indiquer qu'un nouveau message a été publié sur notre contrat intelligent. - -Enfin, appelons notre écouteur dans notre fonction `useEffect` afin qu'il soit initialisé lors du premier rendu du composant `HelloWorld.js`. Dans l'ensemble, votre fonction `useEffect` devrait ressembler à ceci : - +- Sinon, nous utiliserons l'objet `data` renvoyé. L'objet `data.returnValues` est un tableau indexé à zéro où le premier élément du tableau contient le message précédent et le second le message mis à jour. En somme, lors d'un événement réussi, nous définirons notre chaîne `message` sur le message mis à jour, effacerons la chaîne `newMessage` et mettrons à jour notre variable d'état `status` pour indiquer qu'un nouveau message a été publié sur notre contrat intelligent. +Enfin, appelons notre écouteur dans notre fonction `useEffect` afin qu'il soit initialisé lors du premier rendu du composant `HelloWorld.js`. Globalement, votre fonction `useEffect` devrait ressembler à ceci : ```javascript // HelloWorld.js @@ -1346,64 +1109,46 @@ useEffect(async () => { }, []) ``` +Maintenant que nous pouvons lire notre contrat intelligent, il serait formidable de savoir comment y écrire aussi ! Cependant, pour écrire dans notre dapp, nous devons d'abord y connecter un portefeuille Ethereum. -Maintenant que nous sommes capables de lire notre contrat intelligent, il serait bien de savoir comment y écrire aussi ! Cependant, pour écrire sur notre dapp, nous devons d'abord avoir un portefeuille Ethereum connecté à celle-ci. - -Alors, ensuite, nous aborderons la configuration de notre portefeuille Ethereum (MetaMask) et sa connexion à notre dapp ! - - - -### Étape 4 : Configurez votre portefeuille Ethereum {#step-4-set-up-your-ethereum-wallet} - -Pour écrire quoi que ce soit sur la chaîne Ethereum, les utilisateurs doivent signer des transactions à l'aide des clés privées de leur portefeuille virtuel. Pour ce tutoriel, nous utiliserons [MetaMask](https://metamask.io/), un portefeuille virtuel dans le navigateur utilisé pour gérer votre adresse de compte Ethereum, car il rend cette signature de transaction très facile pour l'utilisateur final. - -Si vous voulez en savoir plus sur le fonctionnement des transactions sur Ethereum, consultez [cette page](/developers/docs/transactions/) de la fondation Ethereum. +Donc, nous allons ensuite nous occuper de la configuration de notre portefeuille Ethereum \(MetaMask\) puis de sa connexion à notre dapp ! +### Étape 4 : Configurer votre portefeuille Ethereum {#step-4-set-up-your-ethereum-wallet} +Pour écrire quoi que ce soit sur la chaîne Ethereum, les utilisateurs doivent signer des transactions à l'aide des clés privées de leur portefeuille virtuel. Pour ce tutoriel, nous utiliserons [MetaMask](https://metamask.io/), un portefeuille virtuel dans le navigateur utilisé pour gérer l'adresse de votre compte Ethereum, car il rend cette signature de transaction très facile pour l'utilisateur final. -#### Téléchargez MetaMask {#download-metamask} +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 vers le « Goerli Test Network » en haut à droite \(afin que nous ne traitions pas avec de l'argent réel\). +#### Télécharger MetaMask {#download-metamask} +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 vers le « Réseau de test Goerli » en haut à droite \(afin de ne pas utiliser d'argent réel\). +#### Ajouter de l'ether depuis un robinet {#add-ether-from-a-faucet} -#### Ajoutez de l'ether depuis un Robinet {#add-ether-from-a-faucet} - -Pour signer une transaction sur la blockchain Ethereum, nous aurons besoin de faux Eth. Pour obtenir de l'Eth, vous pouvez aller sur [FaucETH](https://fauceth.komputing.org) et entrer votre adresse de compte Goerli, cliquer sur « Demander des fonds », puis sélectionner « Ethereum Testnet Goerli » dans le menu déroulant et enfin cliquer à nouveau sur le bouton « Demander des fonds ». Vous devriez voir les ETH dans votre compte MetaMask peu de temps après ! - - +Pour signer une transaction sur la blockchain Ethereum, nous aurons besoin de faux Eth. Pour obtenir de l'Eth, vous pouvez vous rendre sur [FaucETH](https://fauceth.komputing.org), saisir l'adresse de votre compte Goerli, cliquer sur « Request funds », puis sélectionner « Ethereum Testnet Goerli » dans le menu déroulant et enfin cliquer à nouveau sur le bouton « Request funds ». Vous devriez voir les ETH dans votre compte MetaMask peu de temps après ! #### Vérifiez 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 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. +**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à ! 🤑 - - -### Étape 5 : Connectez MetaMask à votre interface utilisateur {#step-5-connect-metamask-to-your-UI} +### Étape 5 : Connecter MetaMask à votre interface utilisateur {#step-5-connect-metamask-to-your-UI} Maintenant que notre portefeuille MetaMask est configuré, connectons-y notre dApp ! - - #### La fonction `connectWallet` {#the-connectWallet-function} Dans notre fichier `interact.js`, implémentons la fonction `connectWallet`, que nous pourrons ensuite appeler dans notre composant `HelloWorld.js`. Modifions `connectWallet` comme suit : - - ```javascript // interact.js @@ -1414,7 +1159,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 @@ -1432,8 +1177,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.

@@ -1443,32 +1188,27 @@ export const connectWallet = async () => { } ``` +Alors, que fait exactement ce bloc de code géant ? -Qu'est-ce que cet immense bloc de code fait exactement ? - -Eh bien, premièrement, il vérifie si `window.ethereum` est activé dans votre navigateur. +Eh bien, d'abord, il 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. Si approuvé, il peut lire des données des blockchains auxquelles l'utilisateur est connecté et suggérer que l'utilisateur signe 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é, il peut lire des données des blockchains auxquelles l'utilisateur est connecté, et suggérer à 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. -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. -À l'aide d'une boucle try/catch, nous essaierons 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 un tableau contenant toutes les adresses de 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 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. Maintenant que nous avons écrit cette fonction `connectWallet`, la prochaine étape est de l'appeler dans notre composant `HelloWorld.js`. - - -#### Ajoutez la fonction `connectWallet` à votre composant UI `HelloWorld.js` {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} +#### Ajouter la fonction `connectWallet` à votre composant d'interface `HelloWorld.js` {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} Naviguez vers la fonction `connectWalletPressed` dans `HelloWorld.js`, et mettez-la à jour comme suit : - - ```javascript // HelloWorld.js @@ -1479,31 +1219,26 @@ const connectWalletPressed = async () => { } ``` - Remarquez comment la plupart de nos fonctionnalités sont abstraites de notre composant `HelloWorld.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, sauvegardez les deux fichiers \(`HelloWorld.js` et `interact.js`\) et testez notre interface jusqu'à présent. +Maintenant, enregistrons les deux fichiers \(`HelloWorld.js` et `interact.js`\) et testons notre interface utilisateur jusqu'à présent. Ouvrez votre navigateur sur la page [http://localhost:3000/](http://localhost:3000/), et appuyez sur le bouton « Connecter le portefeuille » en haut à droite de la page. -Si MetaMask est installé, vous devriez être invité à connecter votre portefeuille à votre dApp. Accepter l'invitation à se connecter. +Si MetaMask est installé, vous devriez être invité à connecter votre portefeuille à votre dApp. Acceptez l'invitation à se connecter. -Vous devriez voir que le bouton du portefeuille reflète maintenant le fait que votre adresse est connectée ! Incroyable 🔥 +Vous devriez voir que le bouton du portefeuille indique maintenant que votre adresse est connectée ! Yessss 🔥 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 n'ayez crainte ! Nous pouvons facilement résoudre cela (compris ?) en implémentant la fonction `getCurrentWalletConnected`, qui vérifiera si une adresse est déjà connectée à notre dapp et mettra à jour notre interface en conséquence ! - - +Cependant, n'ayez crainte ! Nous pouvons facilement résoudre ce problème en implémentant `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` {#the-getcurrentwalletconnected-function} Mettez à jour votre fonction `getCurrentWalletConnected` dans le fichier `interact.js` comme suit : - - ```javascript // interact.js @@ -1516,12 +1251,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) { @@ -1538,8 +1273,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.

@@ -1549,15 +1284,12 @@ export const getCurrentWalletConnected = async () => { } ``` - Ce code est _très_ similaire à la fonction `connectWallet` que nous venons d'écrire à l'étape précédente. -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 notre fonction `useEffect` de notre composant `HelloWorld.js` : - - ```javascript // HelloWorld.js @@ -1572,23 +1304,18 @@ 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`. Maintenant que vous avez ajouté ce code, essayons de rafraîchir la fenêtre de notre navigateur. -Magnifique ! 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é ! - +Géniaaaal ! 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é ! - -#### Mettre en œuvre `addWalletListener` {#implement-addwalletlistener} +#### Implémenter `addWalletListener` {#implement-addwalletlistener} 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. Dans votre fichier `HelloWorld.js`, modifiez votre fonction `addWalletListener` comme suit : - - ```javascript // HelloWorld.js @@ -1597,10 +1324,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 { @@ -1608,7 +1335,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.

) @@ -1616,17 +1343,14 @@ function addWalletListener() { } ``` +Je parie que vous n'avez même pas besoin de notre aide pour comprendre ce qui se passe ici à ce stade, mais pour des raisons d'exhaustivité, décomposons rapidement : -Je parie que vous n'avez même pas besoin de notre aide pour comprendre ce qui se passe ici à ce stade, mais pour des raisons de rigueur, décomposons rapidement : - -- 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 et surtout, nous devons l'appeler dans notre fonction `useEffect` : - - ```javascript // HelloWorld.js @@ -1643,30 +1367,23 @@ useEffect(async () => { }, []) ``` +Et c'est tout ! Nous avons terminé avec succès la programmation de toutes les fonctionnalités de notre portefeuille ! Passons maintenant à notre dernière tâche : mettre à jour le message stocké dans notre contrat intelligent ! -Et voilà ! Nous avons réussi à programmer toute notre fonctionnalité de portefeuille ! Passons maintenant à notre dernière tâche : mettre à jour le message stocké dans notre contrat intelligent ! - - - -### Étape 6 : Implémentez la fonction `updateMessage` {#step-6-implement-the-updateMessage-function} - -Alright, nous sommes dans la dernière ligne droite ! Dans la fonction `updateMessage` de votre fichier `interact.js`, nous allons faire ce qui suit : - -1. Vérifiez que le message que nous souhaitons publier dans notre contrat intelligent est valide -2. Signez notre transaction à l'aide de MetaMask -3. Appelez cette fonction depuis notre composant d'interface `HelloWorld.js` +### Étape 6 : Implémenter la fonction `updateMessage` {#step-6-implement-the-updateMessage-function} -Cela ne prendra pas très longtemps ; terminons cette dapp ! +C'est parti, nous sommes dans la dernière ligne droite ! Dans la fonction `updateMessage` de votre fichier `interact.js`, nous allons faire ce qui suit : +1. S'assurer que le message que nous souhaitons publier dans notre contact intelligent est valide +2. Signer notre transaction en utilisant MetaMask +3. Appeler cette fonction depuis notre composant frontend `HelloWorld.js` +Cela ne prendra pas très longtemps ; finissons cette dapp ! -#### Gestion des erreurs d'entrée {#input-error-handling} - -Naturellement, il est logique de disposer d'une sorte de gestion des erreurs d'entrée au début de la fonction. - -Nous souhaitons que notre fonction se termine rapidement s'il n'y a pas d'extension MetaMask installée, si aucun portefeuille n'est connecté \(c'est-à-dire si l'`adresse` transmise est une chaîne vide) ou si le `message` est une chaîne vide. Ajoutons la gestion des erreurs suivante à `updateMessage` : +#### Gestion des erreurs de saisie {#input-error-handling} +Naturellement, il est logique d'avoir une sorte de gestion des erreurs de saisie au début de la fonction. +Nous voudrons que notre fonction se termine prématurément s'il n'y a pas d'extension MetaMask installée, si aucun portefeuille n'est connecté \(c'est-à-dire que l'`address` transmise est une chaîne vide\), ou si le `message` est une chaîne vide. Ajoutons la gestion des erreurs suivante à `updateMessage` : ```javascript // interact.js @@ -1675,40 +1392,35 @@ export const updateMessage = async (address, message) => { if (!window.ethereum || address === null) { return { status: - "💡 Connecter votre portefeuille MetaMask pour mettre à jour le message sur la blockchain.", + "💡 Connectez votre portefeuille MetaMask pour mettre à jour le message sur la blockchain.", } } if (message.trim() === "") { return { - status: "❌ Votre message ne peut pas être vide.", + status: "❌ Votre message ne peut pas être une chaîne vide.", } } } ``` - -Maintenant que nous avons une gestion d'erreur d'entrée appropriée, il est temps de signer la transaction via MetaMask ! - - +Maintenant que nous avons une bonne gestion des erreurs de saisie, il est temps de signer la transaction via MetaMask ! #### Signer notre transaction {#signing-our-transaction} -Si vous êtes déjà à l'aise avec les transactions Ethereum web3 traditionnelles, le code que nous écrirons ensuite vous sera très familier. Sous votre code de gestion d'erreur d'entrée, ajoutez ce qui suit à `updateMessage` : - - +Si vous êtes déjà à l'aise avec les transactions Ethereum traditionnelles de web3, le code que nous écrirons ensuite vous sera très familier. Sous votre code de gestion des erreurs de saisie, ajoutez ce qui suit à `updateMessage` : ```javascript // interact.js -//set up transaction parameters +//configurer les paramètres de la transaction const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: address, // must match user's active address. + to: contractAddress, // Requis sauf lors de la publication de contrats. + from: address, // doit correspondre à l'adresse active de l'utilisateur. data: helloWorldContract.methods.update(message).encodeABI(), } -//sign the transaction +//signer la transaction try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -1719,11 +1431,11 @@ try { ✅{" "} - View the status of your transaction on Etherscan! + Consultez l'état de votre transaction sur Etherscan !
- ℹ️ Once the transaction is verified by the network, the message will be - updated automatically. + ℹ️ Une fois la transaction vérifiée par le réseau, le message sera + mis à jour automatiquement.
), } @@ -1734,50 +1446,47 @@ try { } ``` +Analysons ce qui se passe. Tout d'abord, nous configurons les paramètres de nos transactions, où : -Décortiquons ce qui se passe. Premièrement, nous configurons les paramètres de notre transaction, où : +- `to` spécifie l'adresse du destinataire \(notre contrat intelligent\) +- `from` spécifie le signataire de la transaction, la variable `address` que nous avons passée dans notre fonction +- `data` contient l'appel à la méthode `update` de notre contrat intelligent Hello World, recevant notre variable de chaîne `message` en entrée -- `to` spécifie l'adresse du destinataire \(notre contrat intelligent) -- `from` spécifie le signataire de la transaction, la variable `adresse` que nous avons passée à notre fonction -- `data` contient l'appel à la méthode `update` de notre contrat Hello World, recevant notre variable de chaîne `message` en entrée - -Ensuite, nous faisons un appel en attente, `window.ethereum.request`, où nous demandons à MetaMask de signer la transaction. Remarquez que, aux lignes 11 et 12, nous spécifions notre méthode eth, `eth_sendTransaction` et passons nos `transactionParameters`. +Ensuite, nous effectuons un appel await, `window.ethereum.request`, où nous demandons à MetaMask de signer la transaction. Remarquez, aux lignes 11 et 12, nous spécifions notre méthode eth, `eth_sendTransaction`, et nous passons 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ù la chaîne `status` du JSX invite l'utilisateur à consulter Etherscan pour plus d'informations sur sa transaction. +- Si la transaction réussit, la fonction renverra un objet JSON où la chaîne JSX `status` invite l'utilisateur à consulter Etherscan pour plus d'informations sur sa transaction. - Si la transaction échoue, la fonction renverra un objet JSON où la chaîne `status` relaie le message d'erreur. -Dans l'ensemble, notre fonction `updateMessage` devrait ressembler à cela : - - +Globalement, notre fonction `updateMessage` devrait ressembler à ceci : ```javascript // interact.js export const updateMessage = async (address, message) => { - //input error handling + //gestion des erreurs de saisie if (!window.ethereum || address === null) { return { status: - "💡 Connect your MetaMask wallet to update the message on the blockchain.", + "💡 Connectez votre portefeuille MetaMask pour mettre à jour le message sur la blockchain.", } } if (message.trim() === "") { return { - status: "❌ Your message cannot be an empty string.", + status: "❌ Votre message ne peut pas être une chaîne vide.", } } - //set up transaction parameters + //configurer les paramètres de la transaction const transactionParameters = { - to: contractAddress, // Required except during contract publications. - from: address, // must match user's active address. + to: contractAddress, // Requis sauf lors de la publication de contrats. + from: address, // doit correspondre à l'adresse active de l'utilisateur. data: helloWorldContract.methods.update(message).encodeABI(), } - //sign the transaction + //signer la transaction try { const txHash = await window.ethereum.request({ method: "eth_sendTransaction", @@ -1788,11 +1497,11 @@ export const updateMessage = async (address, message) => { ✅{" "} - View the status of your transaction on Etherscan! + Consultez l'état de votre transaction sur Etherscan !
- ℹ️ Once the transaction is verified by the network, the message will - be updated automatically. + ℹ️ Une fois la transaction vérifiée par le réseau, le message sera + mis à jour automatiquement.
), } @@ -1804,16 +1513,11 @@ export const updateMessage = async (address, message) => { } ``` +Enfin et surtout, nous devons connecter notre fonction `updateMessage` à notre composant `HelloWorld.js`. -Enfin, nous devons connecter notre fonction `updateMessage` à notre composant `HelloWorld.js`. - - - -#### Connectez `updateMessage` à l'interface de `HelloWorld.js` {#connect-updatemessage-to-the-helloworld-js-frontend} - -Notre fonction `onUpdatePressed` devrait émettre un appel en attente à la fonction importée `updateMessage` et modifier la variable d'état `status` pour refléter si notre transaction a réussi ou échoué : - +#### Connecter `updateMessage` au frontend `HelloWorld.js` {#connect-updatemessage-to-the-helloworld-js-frontend} +Notre fonction `onUpdatePressed` devrait faire un appel `await` à la fonction `updateMessage` importée et modifier la variable d'état `status` pour refléter si notre transaction a réussi ou échoué : ```javascript // HelloWorld.js @@ -1824,21 +1528,18 @@ const onUpdatePressed = async () => { } ``` - C'est super propre et simple. Et devinez quoi... VOTRE DAPP EST TERMINÉE !!! -Allez-y et testez le bouton **Update** ! - - +Allez-y et testez le bouton **Mettre à jour** ! -### Créez votre propre DApp personnalisée {#make-your-own-custom-dapp} +### Créez votre propre dapp personnalisée {#make-your-own-custom-dapp} Wooooo, vous êtes arrivé à la fin du tutoriel ! Pour récapituler, vous avez appris à : - Connecter un portefeuille MetaMask à votre projet de dapp -- Lire les données de votre contrat intelligent en utilisant l'API [Web3 d'Alchemy](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) +- Lire des données de votre contrat intelligent en utilisant l'API [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) - Signer des transactions Ethereum en utilisant MetaMask -Maintenant, vous êtes pleinement équipé pour appliquer les compétences de ce tutoriel à la construction de votre propre projet de DApp personnalisé ! Comme toujours, si vous avez des questions, n'hésitez pas à nous demander de l'aide dans le [Discord d'Alchemy](https://discord.gg/gWuC7zB). 🧙‍♂️ +Maintenant, vous êtes pleinement équipé pour appliquer les compétences de ce tutoriel afin de créer votre propre projet de dapp personnalisé ! Comme toujours, si vous avez des questions, n'hésitez pas à nous contacter pour obtenir de l'aide sur le [Discord d'Alchemy](https://discord.gg/gWuC7zB). 🧙‍♂️ -Une fois ce tutoriel terminé, faites-nous savoir comment s'est passée votre expérience ou si vous avez des commentaires en nous identifiant sur Twitter [@alchemyplatform](https://twitter.com/AlchemyPlatform) ! +Une fois que vous avez terminé ce tutoriel, faites-nous savoir comment s'est passée votre expérience ou si vous avez des commentaires en nous identifiant sur Twitter [@alchemyplatform](https://twitter.com/AlchemyPlatform) ! diff --git a/public/content/translations/fr/developers/tutorials/hello-world-smart-contract/index.md b/public/content/translations/fr/developers/tutorials/hello-world-smart-contract/index.md index 874564ac305..c58bfca6db6 100644 --- a/public/content/translations/fr/developers/tutorials/hello-world-smart-contract/index.md +++ b/public/content/translations/fr/developers/tutorials/hello-world-smart-contract/index.md @@ -1,75 +1,71 @@ --- -title: Un Contrat intelligent « Hello World » pour les débutants -description: Tutoriel d'introduction à l'écriture et au déploiement d'un contrat intelligent simple sur Ethereum. +title: "Un contrat intelligent « Hello World » pour les débutants" +description: "Tutoriel d'introduction à l'écriture et au déploiement d'un contrat intelligent simple sur Ethereum." author: "elanh" tags: - - "solidité" - - "hardhat" - - "alchemy" - - "contrats intelligents" - - "déployer" + [ + "solidité", + "hardhat", + "alchemy", + "contrats intelligents", + "déploiement" + ] skill: beginner lang: fr published: 2021-03-31 --- -Si vous débutez dans le développement de blockchain et ne savez pas par où commencer, ou si vous souhaitez uniquement comprendre comment déployer et interagir avec les contrats intelligents, ce guide est fait pour vous. Nous allons parcourir la création et le déploiement d'un contrat intelligent simple sur le réseau de test de Goerli à l'aide d'un portefeuille virtuel [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/), et [Alchemy](https://alchemyapi.io/eth) (ne vous inquiétez pas si vous ne comprenez pas à ce stade ce que cela signifie, nous allons l'expliquer). +Si vous débutez dans le développement de la blockchain et que vous ne savez pas par où commencer, ou si vous voulez simplement comprendre comment déployer des contrats intelligents et interagir avec, ce guide est fait pour vous. Nous allons vous guider dans la création et le déploiement d'un contrat intelligent simple sur le réseau de test Sepolia en utilisant un portefeuille virtuel [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/) et [Alchemy](https://www.alchemy.com/eth) (ne vous inquiétez pas si vous ne comprenez pas encore ce que tout cela signifie, nous vous l'expliquerons). -> **Avertissement ** -> -> 🚧 Avis de fin de support -> -> Tout au long de ce guide, le réseau de test Goerli est utilisé pour créer et déployer un contrat intelligent. Cependant, veuillez noter que l'Ethereum Foundation a annoncé que [Goerli sera bientôt obsolète](https://www.alchemy.com/blog/goerli-faucet-deprecation). -> -> Nous vous recommandons d'utiliser le [Sepolia](https://www.alchemy.com/overviews/sepolia-testnet) et le [distributeur sur Sepolia](https://sepoliafaucet.com/) pour ce tutoriel. +Dans la [partie 2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract) de ce tutoriel, nous verrons comment interagir avec notre contrat intelligent une fois qu'il sera déployé, et dans la [partie 3](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan), nous aborderons la façon de le publier sur Etherscan. -Dans la [partie 2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract) de ce tutoriel, nous allons voir comment nous pouvons interagir avec notre contrat intelligent une fois qu'il sera déployé ici, et dans la [partie 3](https://docs.alchemy.com/docs/submitting-your-smart-contract-to-etherscan) nous couvrirons comment le publier sur Etherscan. - -Si vous avez des questions à un moment ou à un autre, n'hésitez pas à en discuter sur le [Discord Alchemy](https://discord.gg/gWuC7zB)! +Si vous avez des questions à un moment ou à un autre, n'hésitez pas à nous contacter sur le [Discord d'Alchemy](https://discord.gg/gWuC7zB) ! ## Étape 1 : Se connecter au réseau Ethereum {#step-1} -Il existe de nombreuses façons de faire des requêtes dans la chaîne d'Ethereum. Pour plus de simplicité, nous allons utiliser un compte gratuit sur Alchemy, une plateforme de blockchain et d'API pour développeur, nous permettant de communiquer avec la chaîne Ethereum sans avoir à exécuter nos propres nœuds. La plateforme possède également des outils de développement pour la surveillance et l'analyse, dont nous allons tirer parti dans ce tutoriel, pour comprendre ce qui se passe sous le capot de notre déploiement de contrat intelligent. Si vous n'avez pas encore de compte Alchemy, [vous pouvez vous inscrire gratuitement ici](https://dashboard.alchemyapi.io/signup). +Il existe de nombreuses façons de faire des requêtes sur la chaîne Ethereum. Pour des raisons de simplicité, nous utiliserons un compte gratuit sur Alchemy, 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. La plateforme dispose également d'outils de développement pour la surveillance et l'analyse, dont nous tirerons parti dans ce tutoriel pour comprendre ce qui se passe en coulisses lors du déploiement de notre contrat intelligent. Si vous n'avez pas encore de compte Alchemy, [vous pouvez vous inscrire gratuitement ici](https://dashboard.alchemy.com/signup). ## Étape 2 : Créer votre application (et votre clé API) {#step-2} -Une fois que vous avez créé un compte Alchemy, 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 Goerli. Si vous n'êtes pas familier avec Testnets (réseau de test), consultez [cette page](/developers/docs/networks/). +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. Si vous n'êtes pas familier avec les réseaux de test, consultez [cette page](/developers/docs/networks/). + +1. Accédez à la page "Create new app" dans votre tableau de bord Alchemy en sélectionnant "Select an app" dans la barre de navigation et en cliquant sur "Create new app". -1. Accédez à la page « Créer une application » dans votre tableau de bord Alchemy en survolant « Apps » dans la barre de navigation et en cliquant sur « Créer une application » +![Création de l'application Hello world](./hello-world-create-app.png) -![créer une application Hello world](./hello-world-create-app.png) +2. Nommez votre application « Hello World », fournissez une courte description et choisissez un cas d'utilisation, par exemple, "Infra & Tooling." Ensuite, recherchez "Ethereum" et sélectionnez le réseau. -2. Nommez votre application « Hello World », faites-en une description rapide, pour l'environnement sélectionnez « Staging » (utilisé pour la comptabilité de votre application), et pour votre réseau choisissez « Goerli ». +![Vue de création de l'application Hello world](./create-app-view-hello-world.png) -![créer une vue de l'application Hello world](./create-app-view-hello-world.png) +3. Cliquez sur "Next" pour continuer, puis sur “Create app” et c'est tout ! Votre application devrait apparaître dans le menu déroulant de la barre de navigation, avec une clé API que vous pouvez copier. -3. Cliquez sur « Créer l'application » et voilà ! Votre application devrait apparaître dans le tableau. +## Étape 3 : Créer un compte Ethereum (adresse) {#step-3} -## Étape 3 : Créez un compte Ethereum (adresse) {#step-3} +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. En savoir plus sur les [transactions](/developers/docs/transactions/). -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. Plus d'infos sur les [transactions](/developers/docs/transactions/). +Vous pouvez télécharger MetaMask et créer gratuitement un compte Ethereum [ici](https://metamask.io/download). Lorsque vous créez un compte, ou si vous en avez déjà un, veillez à basculer sur le réseau de test "Sepolia" à l'aide du menu déroulant du réseau (afin de ne pas utiliser d'argent réel). -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 Goerli » en haut à droite (afin de ne pas utiliser d'argent réel). +Si Sepolia n'est pas listé, allez dans le menu, puis dans « Advanced » et faites défiler vers le bas pour activer l'option "Show test networks". Dans le menu de sélection du réseau, choisissez l'onglet "Custom" pour trouver une liste de réseaux de test et sélectionnez "Sepolia." -![exemple metamask ropsten](./metamask-ropsten-example.png) +![Exemple de MetaMask Sepolia](./metamask-sepolia-example.png) -## Étape 4 : Ajoutez de l'ether à partir d'un faucet {#step-4} +## Étape 4 : Ajouter de l'ether depuis un faucet {#step-4} -Afin de déployer notre contrat intelligent sur le réseau de test, nous aurons besoin de faux Eth. Pour obtenir des Eth, vous pouvez vous rendre sur le [faucet Goerli](https://goerlifaucet.com/) et vous connecter à votre compte Alchemy et entrer l'adresse de votre portefeuille, puis cliquez sur « Envoyez-moi des Eth ». Cela peut prendre un certain temps pour recevoir votre faux Eth en fonction du trafic sur le réseau. (Au moment de rédiger l'article, cela a pris environ 30 minutes.) Vous devriez voir les Eth dans votre compte Metamask peu de temps après ! +Afin de déployer notre contrat intelligent sur le réseau de test, nous aurons besoin de faux ETH. Pour obtenir des ETH Sepolia, vous pouvez accéder à la page des [détails du réseau Sepolia](/developers/docs/networks/#sepolia) pour consulter une liste de différents faucets. Si l'un d'eux ne fonctionne pas, essayez-en un autre, car ils peuvent parfois être à sec. La réception de vos faux ETH peut prendre un certain temps en raison du trafic réseau. Vous devriez voir les ETH apparaître dans votre compte Metamask peu de temps après ! -## Étape 5 : Vérifiez votre solde {#step-5} +## Étape 5 : Vérifier votre solde {#step-5} -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 composeur 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 présente 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 notre solde, effectuons une requête [eth_getBalance](/developers/docs/apis/json-rpc/#eth_getbalance) en utilisant l'[outil de composition d'Alchemy](https://sandbox.alchemy.com/?network=ETH_SEPOLIA&method=eth_getBalance&body.id=1&body.jsonrpc=2.0&body.method=eth_getBalance&body.params%5B0%5D=&body.params%5B1%5D=latest). 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 : ```json { "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } ``` -> **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 de wei en ETH est : 1 eth = 1018 wei. Donc si nous convertissons 0x2B5E3AF16B1880000 en décimales, nous obtenons 5\*10¹⁸, ce qui équivaut à 5 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 en ETH est la suivante : 1 ETH = 1018 wei. Donc, si nous convertissons 0x2B5E3AF16B1880000 en décimal, nous obtenons 5\*10¹⁸, ce qui équivaut à 5 ETH. > > Ouf ! Notre fausse monnaie est bien là . -## Étape 6 : Initialisez notre projet {#step-6} +## Étape 6 : Initialiser notre projet {#step-6} Pour commencer, nous allons devoir créer un dossier pour notre projet. Ouvrez votre ligne de commande et tapez : @@ -78,13 +74,13 @@ mkdir hello-world cd hello-world ``` -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) (téléchargez également Node.js, car nous en aurons besoin aussi). +Maintenant que nous sommes dans notre dossier de 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, 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 : +La façon dont vous répondez aux questions d'installation n'a pas vraiment d'importance, voici comment nous avons procédé, à titre de référence : ``` package name: (hello-world) @@ -104,28 +100,28 @@ About to write to /Users/.../.../.../hello-world/package.json: "description": "hello world smart contract", "main": "index.js", "scripts": { - "test": "echo \\"Error: no test specified\\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ``` -Approuvez le package.json et nous sommes prêts à démarrer ! +Approuvez le fichier package.json et nous sommes prêts ! -## Étape 7 : Téléchargez [Hardhat](https://hardhat.org/getting-started/#overview) {#step-7} +## Étape 7 : Télécharger [Hardhat](https://hardhat.org/getting-started/#overview) {#step-7} Hardhat est un environnement de développement qui permet de compiler, déployer, tester et déboguer votre logiciel Ethereum. Il aide les développeurs à construire des contrats intelligents et des dApps localement avant de les déployer sur la chaîne en production. -À l'intérieur de notre projet `hello-world`, exécutez : +Dans notre projet `hello-world`, 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éez un projet Hardhat {#step-8} +## Étape 8 : Créer le projet Hardhat {#step-8} Dans notre dossier de projet, exécutez : @@ -133,7 +129,7 @@ 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 @@ -147,72 +143,72 @@ Vous devriez maintenant voir un message de bienvenue ainsi qu'une option pour s 👷 Welcome to Hardhat v2.0.11 👷‍? -Que voulez vous faire ? … +What do you want to do? … Create a sample project ❯ Create an empty hardhat.config.js Quit ``` -Cela va générer un fichier `hardhat.config.js` dans lequel nous allons spécifier tous les paramètres de notre projet (à l'étape 13). +Cela générera pour nous un fichier `hardhat.config.js`, dans lequel nous spécifierons toute la configuration de notre projet (à l'étape 13). -## Étape 9 : Ajoutez des dossiers au projet {#step-9} +## Étape 9 : Ajouter des dossiers de projet {#step-9} -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 ``` -- `contrats/` est l'endroit où nous garderons notre fichier de code de contrat intelligent 'hello world' -- `scripts/` est l'endroit où nous garderons les scripts à déployer et pour interagir avec notre contrat +- `contracts/` est l'endroit où nous conserverons le fichier de code de notre contrat intelligent hello world. +- `scripts/` est l'endroit où nous conserverons les scripts pour déployer notre contrat et interagir avec lui. -## Étape 10 : Écrivez votre contrat {#step-10} +## Étape 10 : Rédiger notre contrat {#step-10} -Vous vous demandez peut-être quand allons nous enfin écrire du code ??? Eh bien, nous y voilà en cette étape 10. +Vous vous demandez peut-être quand allons-nous enfin écrire du code ?? Eh bien, nous y voilà, à l'étape 10. -Ouvrez le projet hello-world 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 utiliserons pour écrire notre contrat intelligent HelloWorld.sol. +Ouvrez le projet hello-world dans votre éditeur de code favori (nous aimons [VSCode](https://code.visualstudio.com/)). Les contrats intelligents sont écrits dans un langage appelé Solidity que nous utiliserons pour écrire notre contrat intelligent HelloWorld.sol. -1. Naviguez vers le dossier « contracts » et créez un nouveau fichier appelé HelloWord.sol -2. Ci-dessous un exemple de contrat intelligent Hello World de la fondation Ethereum que nous utiliserons pour ce tutoriel. Copiez et collez le contenu ci-dessous dans votre fichier HelloWorld.sol et assurez-vous de lire les commentaires pour comprendre ce que fait ce contrat : +1. Accédez au dossier « contracts » et créez un nouveau fichier nommé HelloWorld.sol. +2. Vous trouverez ci-dessous un exemple de contrat intelligent Hello World de la Fondation Ethereum que nous utiliserons pour ce tutoriel. Copiez et collez le contenu ci-dessous dans votre fichier HelloWorld.sol, et assurez-vous de lire les commentaires pour comprendre ce que fait ce contrat : ```solidity -// Specifies the version of Solidity, using semantic versioning. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// Spécifie la version de Solidity, en utilisant le versionnage sémantique. +// En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity ^0.7.0; -// Defines a contract named `HelloWorld`. -// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// Définit un contrat nommé `HelloWorld`. +// Un contrat est un ensemble de fonctions et de données (son état). Une fois déployé, un contrat réside à une adresse spécifique sur la blockchain Ethereum. En savoir plus : https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - // Declares a state variable `message` of type `string`. - // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. + // Déclare une variable d'état `message` de type `string`. + // Les variables d'état sont des variables dont les valeurs sont stockées en permanence dans le stockage du contrat. Le mot-clé `public` rend les variables accessibles depuis l'extérieur d'un contrat et crée une fonction que d'autres contrats ou clients peuvent appeler pour accéder à la valeur. string public message; - // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. - // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + // Semblable à de nombreux langages orientés objet basés sur des classes, un constructeur est une fonction spéciale qui n'est exécutée qu'à la création du contrat. + // Les constructeurs sont utilisés pour initialiser les données du contrat. En savoir plus :https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors constructor(string memory initMessage) { - // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). + // Accepte un argument de chaîne de caractères `initMessage` et définit la valeur dans la variable de stockage `message` du contrat). message = initMessage; } - // A public function that accepts a string argument and updates the `message` storage variable. + // Une fonction publique qui accepte un argument de chaîne de caractères et met à jour la variable de stockage `message`. function update(string memory newMessage) public { message = newMessage; } } ``` -Il s'agit d'un contrat intelligent très simple qui stocke un message lors de la création et peut être mis à jour en appelant la fonction `update`. +Il s'agit d'un contrat intelligent très simple qui stocke un message lors de sa création et qui peut être mis à jour en appelant la fonction `update`. -## Étape 11 : Connectez MetaMask & Alchemy à votre projet {#step-11} +## Étape 11 : Connecter MetaMask et Alchemy à votre projet {#step-11} -Maintenant que nous avons créé un portefeuille Metamask, un compte Alchemy et écrit notre contrat intelligent, il est temps de connecter les trois. +Nous avons créé un portefeuille MetaMask, un compte Alchemy et rédigé notre contrat intelligent. Il est maintenant 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 : @@ -220,19 +216,19 @@ 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 +- Suivez [ces instructions](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/) pour exporter votre clé privée - Voir ci-dessous pour obtenir l'URL de l'API HTTP Alchemy -![obtenir la clé api alchemy](./get-alchemy-api-key.gif) +![obtenir la clé api alchemy](./get-alchemy-api-key.png) Copiez l'URL de l'API Alchemy -Votre `.env` devrait ressembler à ceci : +Votre `.env` devrait ressembler à ceci : ``` -API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY = "your-metamask-private-key" ``` @@ -241,16 +237,16 @@ Pour les relier à notre code, nous ferons référence à ces variables dans not -Ne propagez pas le fichier .env ! Veillez à ne jamais partager ou exposer votre fichier .env avec quiconque car vous compromettez vos secrets en le faisant. Si vous utilisez le contrôle de version, ajoutez votre .env à un fichier gitignore. +Ne commitez pas le fichier .env ! Veillez à ne jamais partager ou exposer votre fichier .env avec quiconque, car vous compromettez vos secrets en le faisant. Si vous utilisez un système de contrôle de version, ajoutez votre fichier .env à un fichier gitignore. -## Étape 12 : Installez Ethers.js {#step-12-install-ethersjs} +## Étape 12 : Installer Ethers.js {#step-12-install-ethersjs} -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 : @@ -260,11 +256,11 @@ 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. -## Étape 13 : Mettez à jour hardhat.config.js {#step-13-update-hardhatconfigjs} +## Étape 13 : Mettre à jour hardhat.config.js {#step-13-update-hardhatconfigjs} -A ce stade, nous avons ajouté plusieurs dépendances et plugins. Nous devons maintenant mettre à jour `hardhat.config.js` pour que notre projet les reconnaisse. +À ce stade, nous avons ajouté plusieurs dépendances et plugins. Nous devons maintenant mettre à jour `hardhat.config.js` pour que notre projet les reconnaisse. -Mettez à jour votre `hardhat.config.js` pour qu'il ressemble à ceci : +Mettez à jour votre `hardhat.config.js` pour qu'il ressemble à ceci : ``` require('dotenv').config(); @@ -277,10 +273,10 @@ const { API_URL, PRIVATE_KEY } = process.env; */ module.exports = { solidity: "0.7.3", - defaultNetwork: "goerli", + defaultNetwork: "sepolia", networks: { hardhat: {}, - goerli: { + sepolia: { url: API_URL, accounts: [`0x${PRIVATE_KEY}`] } @@ -288,9 +284,9 @@ module.exports = { } ``` -## Étape 14 : Compilons notre contrat {#step-14-compile-our-contracts} +## Étape 14 : Compiler notre contrat {#step-14-compile-our-contracts} -Pour s’assurer à ce stade que tout fonctionne, compilons notre contrat. La tâche `compile` est une des tâches intégrées à hardhat. +Pour s’assurer à ce stade que tout fonctionne, compilons notre contrat. La tâche `compile` est l'une des tâches intégrées de hardhat. À partir de la ligne de commande, exécutez : @@ -298,9 +294,9 @@ Pour s’assurer à ce stade que tout fonctionne, compilons notre contrat. La t 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 : Rédigeons notre script de déploiement {#step-15-write-our-deploy-scripts} +## Étape 15 : Rédiger notre script de déploiement {#step-15-write-our-deploy-scripts} 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. @@ -310,9 +306,9 @@ Naviguez vers le dossier `scripts/` et créez un nouveau fichier appelé `deploy async function main() { const HelloWorld = await ethers.getContractFactory("HelloWorld"); - // Start deployment, returning a promise that resolves to a contract object + // Lancer le déploiement, renvoyant une promesse qui se résout en un objet de contrat const hello_world = await HelloWorld.deploy("Hello World!"); - console.log("Contract deployed to address:", hello_world.address);} + console.log("Contrat déployé à l'adresse :", hello_world.address);} main() .then(() => process.exit(0)) @@ -322,48 +318,50 @@ 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 HelloWorld = await ethers.getContractFactory("HelloWorld"); ``` -Une `ContractFactory` dans ethers.js est une abstraction utilisée pour déployer de nouveaux contrats intelligents. Ainsi, `HelloWorld` est ici une usine pour des exemples de notre contrat Hello world. Lors de l'utilisation du plugin `hardhat-ethers`, les instances `ContractFactory` et `Contract` sont connectées par défaut au premier signataire. +Une `ContractFactory` dans ethers.js est une abstraction utilisée pour déployer de nouveaux contrats intelligents. Ainsi, `HelloWorld` est ici une usine pour des exemples de notre contrat Hello world. 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 hello_world = await HelloWorld.deploy(); ``` -Appeler `deploy()` sur un `ContractFactory` va démarrer le déploiement et retourner une `Promise` qui corrige un `Contract`. 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 `Contract`. C'est l'objet qui possède une méthode pour chacune des fonctions de notre contrat intelligent. -## Étape 16 : Déployons notre contrat {#step-16-deploy-our-contract} +## Étape 16 : Déployer notre contrat {#step-16-deploy-our-contract} Nous sommes enfin prêts à déployer notre contrat intelligent ! Naviguez vers la ligne de commande et exécutez : ``` -npx hardhat run scripts/deploy.js --network goerli +npx hardhat run scripts/deploy.js --network sepolia ``` -Vous devriez dès lors voir quelque chose comme : +Vous devriez maintenant voir quelque chose comme : ``` -Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +Contrat déployé à l'adresse : 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 ``` -Si nous allons sur l'[etherscan Goerli](https://goerli.etherscan.io/) et que nous recherchons l'adresse de notre contrat, nous devrions constater qu'il a été déployé avec succès. La transaction ressemblera à ceci : +Si nous allons sur l'[Etherscan Sepolia](https://sepolia.etherscan.io/) et que nous recherchons l'adresse de notre contrat, nous devrions être en mesure de voir qu'il a été déployé avec succès. La transaction ressemblera à quelque chose comme : -![contrat etherscan](./etherscan-contract.png) +![Contrat Etherscan](./etherscan-contract.png) -L'adresse `From` devrait correspondre à votre adresse de compte Metamask et l'adresse To retournera « Contract Creation », mais si nous cliquons dans la transaction, nous verrons notre adresse de contrat dans le champ `To` : +L'adresse `From` devrait correspondre à l'adresse de votre compte MetaMask et l'adresse `To` indiquera « Contract Creation » mais si nous cliquons sur la transaction, nous verrons l'adresse de notre contrat dans le champ `To` : -![transaction etherscan](./etherscan-transaction.png) +![Transaction Etherscan](./etherscan-transaction.png) Félicitations ! Vous venez de déployer un contrat intelligent sur la chaîne 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 disposez de plusieurs applications Alchemy, assurez-vous de filtrer par application et sélectionnez « Hello World ». ![explorateur Hello world](./hello-world-explorer.png) +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 disposez de plusieurs applications Alchemy, assurez-vous de filtrer par application et sélectionnez « Hello World ». +![Explorateur Hello World](./hello-world-explorer.png) -Ici, vous verrez un certain nombre d'appels JSON-RPC que Hardhat/Ethers a réalisés pour nous lorsque nous avons appelé la fonction `.deploy()`. Ici, deux appels importants réalisés sont [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), qui est la demande d'écriture de notre contrat sur la chaîne Goerli, et [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash) qui est une requête pour lire des informations sur notre transaction compte tenu du hachage (un modèle type lors de transactions). Pour en savoir plus sur l'envoi de transactions, consultez ce tutoriel sur [l'envoi de transactions en utilisant Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) +Vous verrez ici un certain nombre d'appels JSON-RPC que Hardhat/Ethers ont effectués en coulisses pour nous lorsque nous avons appelé la fonction `.deploy()`. Deux appels importants à mentionner ici sont [`eth_sendRawTransaction`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-send-raw-transaction), qui est la requête pour réellement écrire notre contrat sur la chaîne Sepolia, et [`eth_getTransactionByHash`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-get-transaction-by-hash) qui est une requête pour lire des informations sur notre transaction en fonction du hachage (un modèle typique lors +des 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 première partie de ce tutoriel. Dans la deuxième partie, nous allons [interagir avec notre contrat intelligent](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#part-2-interact-with-your-smart-contract) en mettant à jour notre message initial et, dans la troisième partie, nous [publierons notre contrat intelligent sur Etherscan](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#optional-part-3-publish-your-smart-contract-to-etherscan) afin que tout le monde sache comment interagir avec lui. +C'est tout pour la première partie de ce tutoriel. Dans la deuxième partie, nous allons [interagir avec notre contrat intelligent](https://www.alchemy.com/docs/interacting-with-a-smart-contract) en mettant à jour notre message initial et, dans la troisième partie, nous [publierons notre contrat intelligent sur Etherscan](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan) afin que tout le monde sache comment interagir avec lui. -**Vous voulez en savoir plus sur Alchemy ? Consultez notre [site web](https://alchemyapi.io/eth). Vous ne voulez manquer aucune mise à jour ? Abonnez-vous à notre newsletter [ici](https://www.alchemyapi.io/newsletter) ! N'oubliez pas également de nous suivre sur [Twitter](https://twitter.com/alchemyplatform) et de rejoindre notre [Discord](https://discord.com/invite/u72VCg3)**. +**Vous voulez en savoir plus sur Alchemy ? Consultez notre [site web](https://www.alchemy.com/eth). Ne ratez plus jamais une mise à jour ? Inscrivez-vous à notre newsletter [ici](https://www.alchemy.com/newsletter) ! Assurez-vous également de rejoindre notre [Discord](https://discord.gg/u72VCg3).**.