diff --git a/public/content/translations/de/developers/docs/standards/tokens/index.md b/public/content/translations/de/developers/docs/standards/tokens/index.md index 86471f4aeb1..361f39b7572 100644 --- a/public/content/translations/de/developers/docs/standards/tokens/index.md +++ b/public/content/translations/de/developers/docs/standards/tokens/index.md @@ -1,13 +1,15 @@ --- title: Token-Standards -description: +description: "Erkunden Sie Ethereum Token Standards, einschließlich ERC-20, ERC-721 und ERC-1155 für fungible und nicht fungible Token." lang: de incomplete: true --- ## Einführung {#introduction} -Einer der vielen Entwicklungsstandards von Ethereum konzentriert sich auf Token-Schnittstellen. Diese Standards tragen dazu bei, dass Smart Contracts kombinierbar bleiben, so zum Beispiel sicherzustellen, wenn ein neues Projekt einen Token ausgibt, dass er mit den bestehenden dezentralen Börsen kompatibel bleibt. +Einer der vielen Entwicklungsstandards von Ethereum konzentriert sich auf Token-Schnittstellen. Diese Standards tragen dazu bei, dass Smart Contracts komponierbar bleiben, sodass, wenn ein neues Projekt einen Token ausgibt, dieser mit bestehenden dezentralisierten Börsen und Anwendungen kompatibel bleibt. + +Token-Standards definieren, wie sich Tokens im gesamten Ethereum-Ökosystem verhalten und interagieren. Sie erleichtern es Entwicklern, zu entwickeln, ohne das Rad neu erfinden zu müssen, und gewährleisten, dass Tokens nahtlos mit Wallets, Börsen und DeFi-Plattformen zusammenarbeiten. Ob im Gaming, in der Governance oder in anderen Anwendungsfällen, diese Standards sorgen für Konsistenz und machen Ethereum besser vernetzt. ## Voraussetzungen {#prerequisites} @@ -18,18 +20,22 @@ Einer der vielen Entwicklungsstandards von Ethereum konzentriert sich auf Token- Hier sind einige der beliebtesten Token-Standards auf Ethereum: -- [ERC-20](/developers/docs/standards/tokens/erc-20/) - Eine Standardschnittstelle für fungible (austauschbare) Token, wie z. B. Voting-Token, Staking-Token oder virtuelle Währungen. -- [ERC-721](/developers/docs/standards/tokens/erc-721/) - Eine Standardschnittstelle für nicht-fungible Token, wie eine Urkunde für ein Kunstwerk oder ein Song. -- [ERC-777](/developers/docs/standards/tokens/erc-777/) - ERC-777 ermöglicht es, zusätzliche Funktionen auf Token aufzusetzen, wie z. B. einen Mixer-Vertrag zur Verbesserung des Transaktionsschutzes oder eine Notfall-Wiederherstellungsfunktion für den Fall, dass Sie Ihre privaten Schlüssel verlieren. -- [ERC-1155](/developers/docs/standards/tokens/erc-1155/) - ERC-1155 ermöglicht einen effizienteren Handel und die Bündelung von Transaktionen und spart damit Kosten. Dieser Token-Standard ermöglicht die Erstellung sowohl von Utility-Token (wie $BNB oder $BAT) als auch von nicht-fungiblen Token wie CryptoPunks. +- [ERC-20](/developers/docs/standards/tokens/erc-20/) – Eine Standardschnittstelle für fungible (austauschbare) Tokens, wie Abstimmungstokens, Staking-Tokens oder virtuelle Währungen. + +### NFT-Standards {#nft-standards} + +- [ERC-721](/developers/docs/standards/tokens/erc-721/) – Eine Standardschnittstelle für nicht-fungible Tokens, wie eine Urkunde für ein Kunstwerk oder ein Lied. +- [ERC-1155](/developers/docs/standards/tokens/erc-1155/) – ERC-1155 ermöglicht einen effizienteren Handel und die Bündelung von Transaktionen und spart damit Kosten. Dieser Token-Standard ermöglicht die Erstellung sowohl von Utility-Token (wie $BNB oder $BAT) als auch von nicht-fungiblen Token wie CryptoPunks. + +Die vollständige Liste der [ERC](https://eips.ethereum.org/erc)-Vorschläge. -## Weiterführende Informationen {#further-reading} +## Weiterführende Lektüre {#further-reading} -_Kennen Sie einen Community-Beitrag, der Ihnen geholfen hat? Editieren Sie diese Seite und füge Sie ihn hinzu!_ +_Sie kennen Community-Resourcen die Ihnen geholfen haben? Bearbeiten Sie diese Seite und fügen Sie sie hinzu!_ -## Ähnliche Tutorials {#related-tutorials} +## Verwandte Tutorials {#related-tutorials} -- [Token-Integration-Checkliste](/developers/tutorials/token-integration-checklist/) _– Eine Checkliste der Dinge, die bei der Interaktion mit Token berücksichtigt werden sollen._ -- [Deployment Ihres ersten Smart Contracts](/developers/tutorials/understand-the-erc-20-token-smart-contract/) _– Eine Einführung in die Bereitstellung Ihres ersten Smart Contracts in einem Ethereum-Testnetzwerk._ -- [Übertragung und Freigabe von ERC20-Token aus einem Solidity-Smart-Contract](/developers/tutorials/transfers-and-approval-of-erc-20-tokens-from-a-solidity-smart-contract/) _- Wie man einen Smart Contract verwendet, um mit einem Token unter Verwendung der Solidity-Sprache zu interagieren._ -- [Implementierung eines ERC721 Marktes [eine Anleitung]](/developers/tutorials/how-to-implement-an-erc721-market/) _– Wie man Token-Artikel dezentralisiert verkauft._ +- [Checkliste für die Token-Integration](/developers/tutorials/token-integration-checklist/) _– Eine Checkliste mit Dingen, die bei der Interaktion mit Tokens zu beachten sind._ +- [Den Smart Contract des ERC20-Tokens verstehen](/developers/tutorials/understand-the-erc-20-token-smart-contract/) _– Eine Einführung in die Bereitstellung Ihres ersten Smart Contracts in einem Ethereum-Testnet._ +- [Übertragungen und Genehmigung von ERC20-Tokens aus einem Solidity-Smart-Contract](/developers/tutorials/transfers-and-approval-of-erc-20-tokens-from-a-solidity-smart-contract/) _– Wie man einen Smart Contract verwendet, um mit einem Token mithilfe der Programmiersprache Solidity zu interagieren._ +- [Implementierung eines ERC721-Marktes [eine Anleitung]](/developers/tutorials/how-to-implement-an-erc721-market/) _– Wie man tokenisierte Artikel auf einer dezentralen Kleinanzeigenplattform zum Verkauf anbietet._ diff --git a/public/content/translations/de/developers/docs/storage/index.md b/public/content/translations/de/developers/docs/storage/index.md index 9dc977599d4..85f3aa17cdf 100644 --- a/public/content/translations/de/developers/docs/storage/index.md +++ b/public/content/translations/de/developers/docs/storage/index.md @@ -1,12 +1,12 @@ --- title: Dezentrale Speicher -description: Übersicht über dezentrale Speicher und verfügbare Tools für die Integration der Speicher in eine dApp +description: "Übersicht über dezentrale Speicher und verfügbare Tools für die Integration der Speicher in eine dApp" lang: de --- Im Gegensatz zu einem zentralisierten Server, der von einem einzelnen Unternehmen oder einer Organisation betrieben wird, bestehen dezantrale Speichersysteme aus einem Peer-to-Peer-Netzwerk, aufgebaut aus Nutzern, die einen Teil der gesamten Daten aufbewahren. Das macht das Speichersystem sehr robust. Diese können in Blockchain-basierten Anwendungen oder jedem anderen Peer-to-Peer Netzwerk sein. -Ethereum selbst kann als dezentrales Speichersystem genutzt werden und das wird es zum Speichern von Code als Smart Contracts auch. Doch Ethereum wurde nicht für größere Datenmengen konzipiert. Die Chain wächst ständig – aktuell umfasst die Ethereum-Chain ca. 500 GB bis 1TB ([je nach Client](https://etherscan.io/chartsync/chaindefault)) und jeder Node im Netzwerk muss in der Lage sein, all diese Daten zu speichern. Würde die Chain immer weiter expandieren (sagen wir mal 5 TB), dann wäre es nicht mehr möglich, dass alle Nodes weiter laufen. Außerdem würden die Kosten, eine solche Datenmenge für das Mainnet bereitzustellen, wegen der [Ressourcengebühren](/developers/docs/gas) unerschwinglich hoch sein. +Ethereum selbst kann als dezentrales Speichersystem genutzt werden und das wird es zum Speichern von Code als Smart Contracts auch. Doch Ethereum wurde nicht für größere Datenmengen konzipiert. Die Chain wächst ständig, aber zum Zeitpunkt der Erstellung dieses Artikels ist die Ethereum-Chain ca. 500 GB – 1 TB groß ([je nach Client](https://etherscan.io/chartsync/chaindefault)), und jeder Node im Netzwerk muss in der Lage sein, alle Daten zu speichern. Würde die Chain immer weiter expandieren (sagen wir mal 5 TB), dann wäre es nicht mehr möglich, dass alle Nodes weiter laufen. Außerdem wären die Kosten für die Bereitstellung dieser Datenmenge im Mainnet aufgrund von [Gas](/developers/docs/gas)-Gebühren unerschwinglich hoch. Aufgrund dieser Einschränkungen ist eine andere Chain oder Methode erforderlich, um große Datenmengen dezentral abzuspeichern. @@ -15,17 +15,17 @@ Bei dezentralen Speichersystemen (dStorage) gibt es ein paar Aspekte, die Sie be - Persistenzmechanismus/Anreizstruktur - Durchsetzung der Datenspeicherung - Dezentralität -- Konsensmechanismus +- Konsens -## Persistenzmechanismus/Anreizstruktur {#persistence-mechanism} +## Persistenzmechanismus / Anreizstruktur {#persistence-mechanism} ### Blockchain-basiert {#blockchain-based} Damit Daten für immer persistent sind, müssen wir uns einen Persistenzmechanismus zunutze machen. Auf Ethereum besteht der Mechanismus zur Persistenz darin, dass die gesamte Chain beim Betrieb eines Nodes berücksichtigt beziehungsweise heruntergeladen werden muss. Neue Daten werden am Ende der Kette angehängt und die Kette wächst weiter – jeder Node muss alle eingebetteten Daten replizieren. -Das wird als **Blockchain-basierte** Persistenz bezeichnet. +Dies wird als **blockchain-basierte** Persistenz bezeichnet. -Das Problem bei der Blockchain-basierten Persistenz ist, dass die Kette viel zu groß werden könnte, um alle Daten aufrechtzuerhalten und zu speichern (z. B. schätzen [viele Quellen](https://healthit.com.au/how-big-is-the-internet-and-how-do-we-measure-it/), dass das Internet über 40 Zetabyte Speicherkapazität benötigt). +Das Problem bei der Blockchain-basierten Persistenz ist, dass die Chain viel zu groß werden könnte, um alle Daten praktikabel zu verwalten und zu speichern (z. B. schätzen [viele Quellen](https://healthit.com.au/how-big-is-the-internet-and-how-do-we-measure-it/), dass das Internet über 40 Zettabyte Speicherkapazität benötigt). Die Blockchain benötigt außerdem auch eine Art Anreizstruktur. Bei der Blockchain-basierten Persistenz wird eine Zahlung an den Validator durchgeführt. Wenn Daten zur Blockchain hinzugefügt werden, werden die Validatoren für das Hinzufügen der Daten bezahlt. @@ -36,40 +36,40 @@ Plattformen mit Blockchain-basierter Persistenz: ### Vertragsbasiert {#contract-based} -**Vertragsbasierte** Persistenz ist dazu gedacht, dass Daten nicht von jedem Node repliziert und für immer gespeichert werden können, sondern durch vertragliche Vereinbarungen aufrechterhalten werden müssen. Das sind Vereinbarungen zwischen mehreren Nodes, die einander versprechen, bestimmte Daten für einen festgelegten Zeitraum verfügbar zu halten. Wenn die Vereinbarungen auslaufen, müssen sie beendet oder erneuert werden, um die Datenpersistenz zu garantieren. +Die **vertragsbasierte** Persistenz geht davon aus, dass Daten nicht von jedem Node repliziert und für immer gespeichert werden können, sondern stattdessen durch vertragliche Vereinbarungen aufrechterhalten werden müssen. Das sind Vereinbarungen zwischen mehreren Nodes, die einander versprechen, bestimmte Daten für einen festgelegten Zeitraum verfügbar zu halten. Wenn die Vereinbarungen auslaufen, müssen sie beendet oder erneuert werden, um die Datenpersistenz zu garantieren. -In den meisten Fällen werden nicht alle Daten in der Chain gespeichert, sondern nur der Hashwert der Position, an der sich die Daten in einer Chain befinden. So muss nicht die gesamte Chain skalieren, um alle Daten zu speichern. +In den meisten Fällen wird statt aller Daten auf der Blockchain nur der Hash gespeichert, der angibt, wo die Daten auf einer Kette zu finden sind. So muss nicht die gesamte Chain skalieren, um alle Daten zu speichern. Plattformen mit vertragsbsierter Persistenz: -- [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/) -- [Crust-Netzwerk](https://crust.network) +- [Crust Network](https://crust.network) - [Swarm](https://www.ethswarm.org/) - [4EVERLAND](https://www.4everland.org/) -### Weitere Überlegungen {#additional-consideration} +### Zusätzliche Überlegungen {#additional-consideration} IPFS ist ein verteiltes System für die Speicherung und den Zugriff auf Dateien, Websites, Anwendungen und Daten. Es hat kein eingebautes Anreizsystem, sondern kann stattdessen mit einer der oben genannten vertragsbasierten Anreizlösungen für eine längerfristige Persistenz verwendet werden. Eine andere Möglichkeit, Daten auf IPFS zu halten, ist die Zusammenarbeit mit einem Pinning-Dienst, der Ihre Daten für Sie "anpinnt". Sie können sogar Ihren eigenen IPFS-Node betreiben und zum Netzwerk beitragen, um Ihre und/oder die Daten anderer kostenlos aufzubewahren. - [IPFS](https://docs.ipfs.io/concepts/what-is-ipfs/) -- [Pinata](https://www.pinata.cloud/) _(IPFS Pinning Service)_ -- [web3.storage](https://web3.storage/) _(IPFS/Filecoin Pinning Service)_ -- [Infura](https://infura.io/product/ipfs) _(IPFS Pinning Service)_ -- [IPFS Scan](https://ipfs-scan.io) _(IPFS Pinning Explorer)_ -- [4EVERLAND](https://www.4everland.org/)_(IPFS Pinning Service)_ -- [Filebase](https://filebase.com) _(IPFS Pinning Service)_ -- [Spheron Network](https://spheron.network/) _(IPFS-/Filecoin-Pinning-Dienst)_ +- [Pinata](https://www.pinata.cloud/) _(IPFS-Pinning-Dienst)_ +- [web3.storage](https://web3.storage/) _(IPFS/Filecoin-Pinning-Dienst)_ +- [Infura](https://infura.io/product/ipfs) _(IPFS-Pinning-Dienst)_ +- [IPFS Scan](https://ipfs-scan.io) _(IPFS-Pinning-Explorer)_ +- [4EVERLAND](https://www.4everland.org/) _(IPFS-Pinning-Dienst)_ +- [Filebase](https://filebase.com) _(IPFS-Pinning-Dienst)_ +- [Spheron Network](https://spheron.network/) _(IPFS/Filecoin-Pinning-Dienst)_ SWARM ist eine dezentrale Datenspeicherungs- und Datenverteilungstechnologie mit einem Speicher-Incentive-System und einem Speicher-Mietpreis-Orakel. -## Datenaufbewahrung/Verfügbarkeit {#data-retention} +## Datenaufbewahrung {#data-retention} Systeme müssen für die Datenverfügbarkeit über einen Mechanismus verfügen, der genau dies sicherstellt. -### Herausforderungsmechanismus {#challenge-mechanism} +### Challenge-Mechanismus {#challenge-mechanism} Einer der beliebtesten Wege zur Gewährleistung der Datenverfügbarkeit ist es, irgendeine Art von kryptographischer Herausforderung an die Knoten zu senden, die sie nur lösen können, wenn die Daten noch vorhanden sind. Ein einfaches Beispiel ist der Proof-of-Access von Arweave. Sie senden eine Herausforderung an Knoten, um zu sehen, ob sie über diese die Daten im aktuellsten Block sowie einem zufälligen Block in der Vergangenheit verfügen. Hat ein Knoten nicht die richtige Antwort, wird er bestraft. @@ -79,7 +79,7 @@ Anbieter von dStorage mit einem Herausforderungsmechanismus: - Skynet - Arweave - Filecoin -- Crust Netzwerk +- Crust-Netzwerk - 4EVERLAND ### Dezentralität {#decentrality} @@ -88,18 +88,17 @@ Es gibt keine hervorragenden Tools, um den Grad der Dezentralität von Plattform Dezentrale Tools ohne KYC: -- Züs (gerade erfolgt die Implementierung einer Version ohne KYC) - Skynet - Arweave - Filecoin - IPFS - Ethereum -- Crust Netzwerk +- Crust-Netzwerk - 4EVERLAND ### Konsens {#consensus} -Die meisten diesere Tools haben ihre eigene Version eines [Konsensmechanismus](/developers/docs/consensus-mechanisms/), basieren aber typischerweise auf [**Proof-of-Work (PoW)**](/developers/docs/consensus-mechanisms/pow/) oder [**Proof-of-Stake (PoS)**](/developers/docs/consensus-mechanisms/pos/). +Die meisten dieser Tools haben ihre eigene Version eines [Konsensmechanismus](/developers/docs/consensus-mechanisms/), aber im Allgemeinen basieren sie entweder auf [**Proof-of-Work (PoW)**](/developers/docs/consensus-mechanisms/pow/) oder [**Proof-of-Stake (PoS)**](/developers/docs/consensus-mechanisms/pos/). Basierend auf Proof-of-Work: @@ -111,56 +110,56 @@ Basierend auf Proof-of-Stake: - Ethereum - Filecoin - Züs -- Crust Netzwerk +- Crust-Netzwerk -## Verwandte Werkzeuge {#related-tools} +## Zugehörige Werkzeuge {#related-tools} -**IPFS – _InterPlanetary File System ist dezentrales Speicher- und Datei-Referenzierungssystem für Ethereum_** +**IPFS – _InterPlanetary File System ist ein dezentrales Speicher- und Dateireferenzierungssystem für Ethereum._** - [Ipfs.io](https://ipfs.io/) - [Dokumentation](https://docs.ipfs.io/) - [GitHub](https://github.com/ipfs/ipfs) -**Storj DCS – _Sicherer, privater und S3-kompatibler dezentraler Cloudobjektspeicher für Entwickler_** +**Storj DCS – _Sicherer, privater und S3-kompatibler dezentraler Cloud-Objektspeicher für Entwickler._** - [Storj.io](https://storj.io/) - [Dokumentation](https://docs.storj.io/) - [GitHub](https://github.com/storj/storj) -**Skynet – _Skynet ist eine dezentrale PoW-Chain speziell für ein dezentrales Web_** +**Sia – _Nutzt Kryptografie, um einen vertrauenslosen Cloud-Speichermarktplatz zu schaffen, der es Käufern und Verkäufern ermöglicht, direkt zu handeln._** -- [Skynet.net](https://siasky.net/) -- [Dokumentation](https://siasky.net/docs/) -- [GitHub](https://github.com/SkynetLabs/) +- [Skynet.net](https://sia.tech/) +- [Dokumentation](https://docs.sia.tech/) +- [GitHub](https://github.com/SiaFoundation/) -**Filecoin – _Erstellt vom dem Team, das hinter IPFS steht. Es handelt sich um eine Anreizebene, aufbauend auf den IPFS-Idealen._** +**Filecoin – _Filecoin wurde von demselben Team entwickelt, das auch hinter IPFS steht._** Es handelt sich um eine Anreizebene, aufbauend auf den IPFS-Idealen._\*\* - [Filecoin.io](https://filecoin.io/) - [Dokumentation](https://docs.filecoin.io/) - [GitHub](https://github.com/filecoin-project/) -**Arweave – _Arweave ist eine dStorage-Plattform für die Datenspeicherung_** +**Arweave – _Arweave ist eine dStorage-Plattform zur Datenspeicherung._** - [Arweave.org](https://www.arweave.org/) - [Dokumentation](https://docs.arweave.org/info/) - [Arweave](https://github.com/ArweaveTeam/arweave/) -**Züs – _Züs ist eine Proof-of-Stake-dStorage-Plattform mit Sharding und Blobbers._** +**Züs – _Züs ist eine Proof-of-Stake dStorage-Plattform mit Sharding und Blobbers._** - [zus.network](https://zus.network/) -- [Documentation](https://0chaindocs.gitbook.io/zus-docs) +- [Dokumentation](https://docs.zus.network/zus-docs/) - [GitHub](https://github.com/0chain/) -**Crust Netzwerk – _Crust ist eine dStorage Plattform auf dem IPFS._** +**Crust Network – _Crust ist eine dStorage-Plattform auf Basis von IPFS._** - [Crust.network](https://crust.network) - [Dokumentation](https://wiki.crust.network) - [GitHub](https://github.com/crustio) -**Swarm – _Ein verteiltes Speichersystem und Content-Verteilungs-Service für den Ethereum-Web3-Stack._** +**Swarm – _Eine verteilte Speicherplattform und ein Dienst zur Inhaltsverteilung für den Ethereum Web3-Stack._** - [EthSwarm.org](https://www.ethswarm.org/) -- [Dokumentation](https://docs.ethswarm.org/docs/) +- [Dokumentation](https://docs.ethswarm.org/) - [GitHub](https://github.com/ethersphere/) **OrbitDB – _Eine dezentrale Peer-to-Peer-Datenbank, die auf IPFS basiert._** @@ -169,48 +168,48 @@ Basierend auf Proof-of-Stake: - [Dokumentation](https://github.com/orbitdb/field-manual/) - [GitHub](https://github.com/orbitdb/orbit-db/) -**Aleph.im – _Dezentrales Cloudprojekt (Datenbanken, Dateispeicherung, Computing und DID). Eine einzigartige Mischung aus Peer-to-Peer-Technologie – Off-Chain und On-Chain. IPFS und Multi-Chain-Kompatibilität._** +**Aleph.im – _Dezentrales Cloud-Projekt (Datenbank, Dateispeicher, Computing und DID)._** Eine einzigartige Mischung aus Peer-to-Peer-Technologie – Off-Chain und On-Chain. IPFS und Multi-Chain-Kompatibilität._\*\* -- [Aleph.im](https://aleph.im/) -- [Dokumentation](https://aleph.im/#/developers/) +- [Aleph.im](https://aleph.cloud/) +- [Dokumentation](https://docs.aleph.cloud/) - [GitHub](https://github.com/aleph-im/) -**Ceramic – _Nutzergesteuerte IPFS-Datenbankspeicher für datenintensive und anspruchsvolle Anwendungen._** +**Ceramic – _Nutzergesteuerter IPFS-Datenbankspeicher für datenintensive und ansprechende Anwendungen._** - [Ceramic.network](https://ceramic.network/) -- [Dokumentation](https://developers.ceramic.network/learn/welcome/) +- [Dokumentation](https://developers.ceramic.network/) - [GitHub](https://github.com/ceramicnetwork/js-ceramic/) -**Filebase – _Ein S3-kompatibler dezentraler Speicher und geo-redundanter IPFS-Pinning-Service. Alle über Filebase auf IPFS hochgeladenen Dateien werden automatisch an die Filebase-Infrastruktur mit dreifacher Replikation auf der ganzen Welt gepinnt._** +**Filebase – _S3-kompatibler dezentraler Speicher und georedundanter IPFS-Pinning-Dienst. Alle über Filebase auf IPFS hochgeladenen Dateien werden automatisch mit 3-facher globaler Replikation an die Filebase-Infrastruktur gepinnt._** - [Filebase.com](https://filebase.com/) - [Dokumentation](https://docs.filebase.com/) - [GitHub](https://github.com/filebase) -**4EVERLAND - _Eine Web 3.0-Cloud-Computing-Plattform, die Speicher-, Rechen- und Netzwerk-Kernfunktionen integriert, S3-kompatibel ist und synchrone Datenspeicherung auf dezentralen Speichernetzwerken wie IPFS und Arweave bietet._** +**4EVERLAND – _Eine Web 3.0-Cloud-Computing-Plattform, die Speicher-, Rechen- und Netzwerk-Kernfunktionen integriert, S3-kompatibel ist und eine synchrone Datenspeicherung auf dezentralen Speichernetzwerken wie IPFS und Arweave bietet._** - [4everland.org](https://www.4everland.org/) - [Dokumentation](https://docs.4everland.org/) - [GitHub](https://github.com/4everland) -**Kaleido – _Eine Blockchain-as-a-Service-Plattform mit IPFS-Knoten auf einen Klick_** +**Kaleido – _Eine Blockchain-as-a-Service-Plattform mit IPFS-Nodes per Mausklick._** - [Kaleido](https://kaleido.io/) - [Dokumentation](https://docs.kaleido.io/kaleido-services/ipfs/) - [GitHub](https://github.com/kaleido-io) -**Spheron Network – _Spheron ist eine Platform-as-a-Service (PaaS), die für dApps entwickelt wurde, die ihre Anwendungen auf dezentraler Infrastruktur mit bester Leistung starten möchten. Sie bietet standardmäßig Rechenleistung, dezentrale Speicherung, CDN und Webhosting._** +**Spheron Network – _Spheron ist eine Platform-as-a-Service (PaaS), die für Dapps entwickelt wurde, die ihre Anwendungen auf einer dezentralen Infrastruktur mit bester Leistung starten möchten. Sie bietet standardmäßig Rechenleistung, dezentralen Speicher, CDN und Webhosting._** - [spheron.network](https://spheron.network/) - [Dokumentation](https://docs.spheron.network/) - [GitHub](https://github.com/spheronFdn) -## Weiterführende Informationen {#further-reading} +## Weiterführende Lektüre {#further-reading} -- [Was sind dezentrale Speichersysteme?](https://coinmarketcap.com/alexandria/article/what-is-decentralized-storage-a-deep-dive-by-filecoin) – _CoinMarketCap_ -- [Fünf gängige Mythen über dezentrale Speichersysteme entlarvt](https://www.storj.io/blog/busting-five-common-myths-about-decentralized-storage) – _Storj_ +- [Was ist dezentraler Speicher?](https://coinmarketcap.com/academy/article/what-is-decentralized-storage-a-deep-dive-by-filecoin) – _CoinMarketCap_ +- [Fünf verbreitete Mythen über dezentralen Speicher widerlegt](https://www.storj.io/blog/busting-five-common-myths-about-decentralized-storage) – _Storj_ -_Kennen Sie eine Community-Ressource, die Ihnen geholfen hat? Bearbeiten Sie diese Seite und fügen Sie sie hinzu._ +_Sie kennen Community-Resourcen die Ihnen geholfen haben? Bearbeiten Sie diese Seite und fügen Sie sie hinzu!_ ## Verwandte Themen {#related-topics} diff --git a/public/content/translations/de/developers/docs/transactions/index.md b/public/content/translations/de/developers/docs/transactions/index.md index ef90d19d88f..565855a4df0 100644 --- a/public/content/translations/de/developers/docs/transactions/index.md +++ b/public/content/translations/de/developers/docs/transactions/index.md @@ -1,6 +1,6 @@ --- title: Transaktionen -description: Eine Übersicht über die Transaktionen von Ethereum – wie sie arbeiten, ihre Datenstruktur und wie sie über eine App gesendet werden. +description: "Eine Übersicht über die Transaktionen von Ethereum – wie sie arbeiten, ihre Datenstruktur und wie sie über eine App gesendet werden." lang: de --- @@ -8,13 +8,14 @@ Transaktionen sind kryptographisch signierte Anweisungen von Konten. Ein Konto w ## Voraussetzungen {#prerequisites} -Um dir zu helfen, diese Seite besser zu verstehen, empfehlen wir dir, zuerst [ Konten](/developers/docs/accounts/), [Transaktionen](/developers/docs/transactions/) und unsere [Einführung in Ethereum](/developers/docs/intro-to-ethereum/) zu lesen. +Um diese Seite besser zu verstehen, empfehlen wir dir, zuerst [Konten](/developers/docs/accounts/) und unsere [Einführung in Ethereum](/developers/docs/intro-to-ethereum/) zu lesen. ## Was ist eine Transaktion? {#whats-a-transaction} Eine Transaktion von Ethereum bezieht sich auf eine Aktion, die von einem externen Konto initiiert wird; mit anderen Worten auf ein Konto, das von einem Menschen verwaltet wird und nicht von einem Vertrag. Wenn zum Beispiel Bob Alice 1 ETH sendet, muss Bobs Konto belastet werden und das von Alice muss eine Gutschrift erhalten. Diese zustandsverändernde Aktion findet innerhalb einer Transaktion statt. -![Diagramm mit einer Zustandsänderung aus einer Transaktion](./tx.png) _Diagramm angepasst von [Ethereum EVM illustriert](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagramm, das eine Zustandsänderung durch eine Transaktion zeigt](./tx.png) +_Diagramm adaptiert von [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Transaktionen, die den Zustand der EVM verändern, müssen auf das gesamte Netzwerk übertragen werden. Jeder Knoten kann eine Anfrage zur Ausführung einer Transaktion an die EVM senden, woraufhin ein Validator die Transaktion ausführt und die daraus resultierende Statusänderung an den Rest des Netzwerks weitergibt. @@ -22,17 +23,17 @@ Transaktionen sind gebührenpflichtig und müssen in einem validierten Block ent Eine abgeschlossene Transaktion enthält folgende Informationen: -- `von` – der Adresse des Senders, der die Transaktion unterzeichnet. Es handelt sich dabei um ein externes Konto, da Vertragskonten keine Transaktionen senden können. -- `to` – die Empfängeradresse (wenn es sich um ein Konto in externem Besitz handelt, wird durch die Transaktion ein Wert übertragen. Bei einem Smart-Contract-Konto führt die Transaktion den Vertragscode aus.) +- `from` – die Adresse des Absenders, der die Transaktion unterzeichnen wird. Es handelt sich dabei um ein externes Konto, da Vertragskonten keine Transaktionen senden können +- `to` – die Empfängeradresse (wenn es sich um ein Konto in externem Besitz handelt, wird die Transaktion einen Wert übertragen. Bei einem Smart-Contract-Konto führt die Transaktion den Vertragscode aus.) - `signature` – die Kennung des Absenders. Das wird generiert, wenn der private Schlüssel des Absenders die Transaktion signiert und bestätigt, dass der Absender diese Transaktion autorisiert hat. -- `nonce` – ein fortlaufend inkrementierender Zähler, der die Transaktionsnummer eines Kontos angibt -- `Wert` – gewünschte Menge an Ether (ETH), die vom Absender an den Empfänger zu überweisen sind (in WEI, ein Ether gleicht 1e + 18wei) -- `input data` – optionales Feld für die Eingabe beliebiger Daten -- `gasLimit` – die maximale Menge an Gaseinheiten, die von der Transaktion verbraucht werden können. Die [EVM](/developers/docs/evm/opcodes) gibt die Gas-Einheiten an, die für jeden Berechnungsschritt benötigt werden -- `maxPriorityFeePerGas` – der Höchstpreis des verbrauchten Gas, der als Trinkgeld an den Validierer weitergegeben wird -- `maxFeePerGas` – die maximale Gebühr pro Gas-Einheit, die für die Transaktion gezahlt werden soll (einschließlich `baseFeePerGas` und `maxPriorityFeePerGas`) +- `nonce` – ein sequenziell inkrementierender Zähler, der die Transaktionsnummer des Kontos angibt +- `value` – der Betrag an ETH, der vom Absender an den Empfänger überwiesen werden soll (in WEI, wobei 1 ETH 1e+18 wei entspricht) +- `input data` – optionales Feld zur Aufnahme beliebiger Daten +- `gasLimit` – die maximale Menge an Gaseinheiten, die von der Transaktion verbraucht werden können. Die [EVM](/developers/docs/evm/opcodes) gibt die für jeden Berechnungsschritt erforderlichen Gaseinheiten an +- `maxPriorityFeePerGas` – der Höchstpreis des verbrauchten Gases, der als Trinkgeld für den Validator enthalten ist +- `maxFeePerGas` – die maximale Gebühr pro Gaseinheit, die für die Transaktion zu zahlen ist (einschließlich `baseFeePerGas` und `maxPriorityFeePerGas`) -Gas ist ein Hinweis auf die Berechnung, die für die Bearbeitung der Transaktion durch einen Validierer erforderlich ist. Benutzer müssen für diese Berechnung eine Gebühr bezahlen. Das `gasLimit` und `maxPriorityFeePerGas` bestimmen die maximale Transaktionsgebühr, die an den Validator gezahlt wird. [Mehr zu Gas](/developers/docs/gas/). +Gas ist ein Hinweis auf die Berechnung, die für die Bearbeitung der Transaktion durch einen Validierer erforderlich ist. Benutzer müssen für diese Berechnung eine Gebühr bezahlen. Das `gasLimit` und `maxPriorityFeePerGas` bestimmen die maximale Transaktionsgebühr, die an den Validator gezahlt wird. [Mehr über Gas](/developers/docs/gas/). Das Transaktionsobjekt wird in etwa wie folgt aussehen: @@ -52,7 +53,7 @@ Aber ein Transaktionsobjekt muss mit dem privaten Schlüssel des Absenders signi Ein Ethereum-Client wie Geth wird diesen Signaturprozess bearbeiten. -Beispiel-[JSON-RPC](/developers/docs/apis/json-rpc)-Aufruf: +Beispiel für einen [JSON-RPC](/developers/docs/apis/json-rpc)-Aufruf: ```json { @@ -99,22 +100,26 @@ Beispielantwort: } ``` -- `raw` ist die signierte Transaktion in [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp) in kodierter Form. -- Das `tx` ist die signierte Transaktion im JSON-Format. +- `raw` ist die signierte Transaktion in [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp)-kodierter Form +- `tx` ist die signierte Transaktion in JSON-Form Mit dem Signatur-Hash kann für die Transaktion kryptographisch nachgewiesen werden, dass sie vom Absender stammt und dem Netzwerk übermittelt wurde. ### Das Datenfeld {#the-data-field} -Die überwiegende Mehrheit der Transaktionen greift auf einen Vertrag über ein externes Konto zu. Die meisten Verträge sind in Solidity geschrieben und interpretieren ihr Datenfeld entsprechedn dem [Application Binary Interface (ABI)](/glossary/#abi). +Die überwiegende Mehrheit der Transaktionen greift auf einen Vertrag über ein externes Konto zu. +Die meisten Verträge sind in Solidity geschrieben und interpretieren ihr Datenfeld gemäß der [Application Binary Interface (ABI)](/glossary/#abi). -Die ersten vier Bytes geben an, welche Funktion aufgerufen werden soll, wobei der Hash des Funktionsnamens und der Argumente verwendet wird. Manchmal kannst du die Funktion anhand des Selektors aus [dieser Datenbank](https://www.4byte.directory/signatures/) identifizieren. +Die ersten vier Bytes geben an, welche Funktion aufgerufen werden soll, wobei der Hash des Funktionsnamens und der Argumente verwendet wird. +Manchmal kannst du die Funktion aus dem Selektor mithilfe [dieser Datenbank](https://www.4byte.directory/signatures/) identifizieren. -Der Rest der Aufrufdaten sind die Argumente, [codiert wie in den ABI-Spezifikationen angegeben](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). +Der Rest der Calldata sind die Argumente, [kodiert wie in den ABI-Spezifikationen angegeben](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). -Betrachten wir zum Beispiel [diese Transaktion](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). Verwende **Für mehr hier klicken**, um die Aufrufdaten zu sehen. +Schauen wir uns zum Beispiel [diese Transaktion](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1) an. +Klicke auf **Click to see More**, um die Calldata zu sehen. -Der Funktions-Selektor ist `0xa9059cbb`. Es gibt mehrere [bekannte Funktionen mit dieser Signatur](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). In diesem Fall wurde [der Contract-Quellcode](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) auf Etherscan hochgeladen, so dass wir wissen, dass die Funktion `transfer(address,uint256)` ist. +Der Funktionsselektor ist `0xa9059cbb`. Es gibt mehrere [bekannte Funktionen mit dieser Signatur](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). +In diesem Fall wurde [der Quellcode des Vertrags](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) auf Etherscan hochgeladen, also wissen wir, dass die Funktion `transfer(address,uint256)` lautet. Der Rest der Daten lautet: @@ -123,11 +128,13 @@ Der Rest der Daten lautet: 000000000000000000000000000000000000000000000000000000003b0559f4 ``` -Entsprechend den ABI-Spezifikationen erscheinen Ganzzahlwerte (wie Adressen, die 20-Byte-Ganzzahlen sind) in ABI als 32-Byte-Wörter, die vorne mit Nullen aufgefüllt werden. Also wissen wir, dass die Adresse von `to` [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279) ist. Der Wert ist `value` 0x3b0559f4 = 990206452. +Entsprechend den ABI-Spezifikationen erscheinen Ganzzahlwerte (wie Adressen, die 20-Byte-Ganzzahlen sind) in ABI als 32-Byte-Wörter, die vorne mit Nullen aufgefüllt werden. +Wir wissen also, dass die `to`-Adresse [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279) ist. +Der `value` ist 0x3b0559f4 = 990206452. ## Arten von Transaktionen {#types-of-transactions} -Bei Ethereum gibt es unterschiedliche Arten von Transaktionen: +Bei Ethereum gibt es ein paar verschiedene Arten von Transaktionen: - Reguläre Transaktionen: eine Transaktion von einem Konto auf ein anderes. - Vertragseinsatz-Transaktionen: eine Transaktion ohne "An"-Adresse, bei der das Datenfeld für den Vertragscode verwendet wird. @@ -135,9 +142,9 @@ Bei Ethereum gibt es unterschiedliche Arten von Transaktionen: ### Über Gas {#on-gas} -Wie bereits erwähnt, kosten das Ausführen von Transaktionen [gas](/developers/docs/gas/). Einfache Überweisungstransaktionen erfordern 21000 Gas. +Wie bereits erwähnt, kostet die Ausführung von Transaktionen [Gas](/developers/docs/gas/). Einfache Überweisungstransaktionen erfordern 21000 Gas. -Damit Bob also Alice 1 ETH zu einer `BasisgebührPerGas` von 190 gwei und einer `maximalenPrioritätsgebührPerGas` von 10 gwei schicken kann, muss er folgende Gebühr bezahlen: +Damit Bob Alice 1 ETH bei einer `baseFeePerGas` von 190 Gwei und einer `maxPriorityFeePerGas` von 10 Gwei senden kann, muss Bob die folgende Gebühr bezahlen: ``` (190 + 10) * 21000 = 4.200.000 gwei @@ -145,35 +152,38 @@ Damit Bob also Alice 1 ETH zu einer `BasisgebührPerGas` von 190 gwei und einer 0,0042 ETH ``` -Bobs Konto wird mit **-1,0042 ETH** belastet (1 ETH für Alice + 0,0042 ETH an Gas-Gebühren) +Bobs Konto wird mit **-1,0042 ETH** belastet (1 ETH für Alice + 0,0042 ETH an Gasgebühren) Alices Konto wird **+1,0 ETH** gutgeschrieben -Die Grundgebühr wird **-0,00399 ETH** verbrannt +Die Grundgebühr wird verbrannt **-0,00399 ETH** -Validatoren behalten das "Trinkgeld" **+0,000210 ETH** +Der Validator behält das Trinkgeld **+0,000210 ETH** - -![Diagramm zeigt, wie ungenutztes Gas zurückerstattet wird](./gas-tx.png) _Diagramm angepasst von [Ethereum EVM illustriert](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagramm, das zeigt, wie ungenutztes Gas zurückerstattet wird](./gas-tx.png) +_Diagramm adaptiert von [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Jedes Gas, das nicht in einer Transaktion verwendet wird, wird auf das Benutzerkonto zurückerstattet. -### Smart Contract-Interaktionen {#smart-contract-interactions} +### Interaktionen mit Smart Contracts {#smart-contract-interactions} Gas wird für jede Transaktion benötigt, die Smart Contracts betrifft. -Smart Contracts können auch Funktionen enthalten, die als [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions) oder [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions) bezeichnet werden; sie verändern nicht den Zustand des Vertrags. Daher ist es nicht erforderlich, Gas zu zahlen, wenn diese Funktionen von einem externen Konto (EOA) aufgerufen werden. Der zugrunde liegende RPC-Aufruf in diesem Szenario ist [`eth_call`](/developers/docs/apis/json-rpc#eth_call) +Smart Contracts können auch Funktionen enthalten, die als [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions)- oder [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions)-Funktionen bekannt sind und den Zustand des Vertrags nicht verändern. Daher ist es nicht erforderlich, Gas zu zahlen, wenn diese Funktionen von einem externen Konto (EOA) aufgerufen werden. Der zugrunde liegende RPC-Aufruf für dieses Szenario ist [`eth_call`](/developers/docs/apis/json-rpc#eth_call). -Im Gegensatz zum Zugriff über `eth_call` werden diese `view`- oder `pure`-Funktionen auch häufig intern aufgerufen (also vom Vertrag selbst oder von einem anderen Vertrag), was jedoch Gas kostet. +Im Gegensatz zum Zugriff über `eth_call` werden diese `view`- oder `pure`-Funktionen auch häufig intern aufgerufen (d.h. vom Vertrag selbst oder von einem anderen Vertrag), was Gas kostet. -## Transaktions-Lebenszyklus {#transaction-lifecycle} +## Lebenszyklus einer Transaktion {#transaction-lifecycle} Sobald die Transaktion abgeschickt wurde, passiert Folgendes: -1. Ein Transaktions-Hash wird kryptografisch erzeugt: `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` +1. Ein Transaktions-Hash wird kryptografisch generiert: + `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` 2. Die Transaktion wird dann an das Netzwerk weitergeleitet und zu einem Transaktionspool hinzugefügt, der aus allen anderen ausstehenden Netztransaktionen besteht. 3. Ein Validator muss Ihre Transaktion auswählen und in einem Block hinzufügen, um die Transaktion zu verifizieren und sie als "erfolgreich" zu bezeichnen. -4. Mit der Zeit wird der Block, der Ihre Transaktion enthält, auf "justified" und dann auf " finalized" hochgestuft. Mit diesen Hochstufungen steigt auch die Sicherheit, dass Ihre Transaktion erfolgreich war und nicht mehr verändert werden kann. Sobald ein Block abgeschlossen, also "finalized", ist, könnte er nur noch durch einen Angriff auf Netzwerkebene verändert werden, der viele Milliarden Dollar kosten würde. +4. Mit der Zeit wird der Block, der Ihre Transaktion enthält, auf "justified" und dann auf " finalized" hochgestuft. Diese Upgrades geben eine viel + größere Sicherheit, dass deine Transaktion erfolgreich war und niemals geändert wird. Sobald ein Block „finalisiert“ ist, kann er nur durch einen + Angriff auf Netzwerkebene geändert werden, der viele Milliarden Dollar kosten würde. ## Eine visuelle Demo {#a-visual-demo} @@ -181,38 +191,40 @@ Schaue Austin bei einer Führung durch Transaktionen, Gas und Mining zu. -## Typisierter Transaktionsumschlag {#typed-transaction-envelope} +## Typisierter Transaktions-Envelope {#typed-transaction-envelope} -Ursprünglich hatte Ethereum ein einziges Format für Transaktionen. Jede Transaktion enthielt eine Nonce, einen Gaspreis, ein Gaslimit, eine Zieladresse, einen Wert, Daten, v, r und s. Diese Felder sind [RLP-kodiert](/developers/docs/data-structures-and-encoding/rlp/) und sehen etwa folgendermaßen aus: +Ursprünglich hatte Ethereum ein einziges Format für Transaktionen. Jede Transaktion enthielt eine Nonce, einen Gaspreis, ein Gaslimit, eine Zieladresse, einen Wert, Daten, v, r und s. Diese Felder sind [RLP-kodiert](/developers/docs/data-structures-and-encoding/rlp/), um etwa so auszusehen: `RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` -Ethereum hat sich so entwickelt, dass es mehrere Transaktionsarten unterstützt, damit neue Funktionen wie Zugriffslisten und [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) implementiert werden können, ohne die alten Transaktionsformate zu beeinflussen. +Ethereum hat sich weiterentwickelt, um mehrere Arten von Transaktionen zu unterstützen, damit neue Funktionen wie Zugriffslisten und [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) implementiert werden können, ohne die alten Transaktionsformate zu beeinträchtigen. -[EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) ermöglicht dieses Verhalten. Transaktionen werden wie folgt interpretiert: +[EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) ist das, was dieses Verhalten ermöglicht. Transaktionen werden wie folgt interpretiert: `TransactionType || TransactionPayload` Die Felder sind wie folgt definiert: -- `TransactionType` – eine Zahl zwischen 0 und 0x7f, für insgesamt 128 mögliche Transaktionsarten. +- `TransactionType` – eine Zahl zwischen 0 und 0x7f, für insgesamt 128 mögliche Transaktionstypen. - `TransactionPayload` – ein beliebiges Byte-Array, das durch den Transaktionstyp definiert wird. Basierend auf dem `TransactionType`-Wert kann eine Transaktion wie folgt klassifiziert werden: -1. **Typ-0-Transaktionen (veraltet):** Das ursprüngliche Transaktionsformat, das seit dem Start von Ethereum verwendet wird. Diese Transaktionen enthalten keine Funktionen aus [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) wie dynamische Gasgebührenkalkulationen oder Zugriffslisten für Smart Contracts. Veraltete Transaktionen haben in ihrer serialisierten Form keinen spezifischen Präfix, der ihren Typ angibt; sie beginnen mit dem Byte `0xf8`, wenn die [Recursive Length Prefix(RLP)](/developers/docs/data-structures-and-encoding/rlp)-Kodierung verwendet wird. Der TransactionType-Wert für diese Transaktionen ist `0x0`. +1. **Typ-0-Transaktionen (Legacy):** Das ursprüngliche Transaktionsformat, das seit dem Start von Ethereum verwendet wird. Sie enthalten keine Funktionen von [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), wie z. B. dynamische Gasgebührenberechnungen oder Zugriffslisten für Smart Contracts. Legacy-Transaktionen fehlt in ihrer serialisierten Form ein spezifisches Präfix, das ihren Typ angibt. Sie beginnen mit dem Byte `0xf8`, wenn die [Recursive Length Prefix (RLP)](/developers/docs/data-structures-and-encoding/rlp)-Kodierung verwendet wird. Der TransactionType-Wert für diese Transaktionen ist `0x0`. -2. **Typ-1-Transaktionen:** Diese Transaktionen wurden in [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) als Teil des [Berlin-Upgrades](/ethereum-forks/#berlin) von Ethereum eingeführt und enthalten einen `accessList`-Parameter. Diese Liste gibt Adressen und Speicherschlüssel an, auf die bei der Transaktion zugegriffen werden soll, was potenziell die [Gas](/developers/docs/gas/)-Kosten für komplexe Transaktionen mit Smart Contracts reduzieren kann. Änderungen des EIP-1559-Gebührenmarkts sind in Typ-1-Transaktionen nicht enthalten. Typ-1-Transaktionen enthalten auch einen `yParity`-Parameter, der entweder `0x0` oder `0x1` sein kann und die Parität des y-Werts der secp256k1-Signatur angibt. Sie werden durch das Anfangs-Byte `0x01` identifiziert und ihr TransactionType-Wert ist `0x1`. +2. **Typ-1-Transaktionen:** Eingeführt in [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) als Teil des [Berlin-Upgrades](/ethereum-forks/#berlin) von Ethereum, enthalten diese Transaktionen einen `accessList`-Parameter. Diese Liste gibt Adressen und Speicherschlüssel an, auf die die Transaktion zugreifen soll, was dazu beitragen kann, die [Gas](/developers/docs/gas/)-Kosten für komplexe Transaktionen mit Smart Contracts zu reduzieren. Änderungen des EIP-1559-Gebührenmarkts sind in Typ-1-Transaktionen nicht enthalten. Typ-1-Transaktionen enthalten auch einen `yParity`-Parameter, der entweder `0x0` oder `0x1` sein kann und die Parität des y-Wertes der secp256k1-Signatur anzeigt. Sie werden durch das Startbyte `0x01` identifiziert, und ihr TransactionType-Wert ist `0x1`. -3. **Typ-2-Transaktionen**, allgemein als EIP-1559-Transaktionen bezeichnet, sind Transaktionen, die in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), dem [London-Upgrade](/ethereum-forks/#london) von Ethereum, eingeführt wurden. Diese haben sich zur Standardform für Transaktionen auf dem Ethereum-Netzwerk entwickelt. Diese Transaktionen führen einen neuen Gebührenmarktmechanismus ein, der durch die Trennung der Transaktionsgebühr in eine Basisgebühr und eine Prioritätsgebühr die Vorhersehbarkeit verbessert. Sie beginnen mit dem Byte `0x02` und enthalten Felder wie `maxPriorityFeePerGas` und `maxFeePerGas`. Typ-2-Transaktionen sind aufgrund ihrer Flexibilität und Effizienz nun der Standard und werden besonders in Zeiten hoher Netzwerkbelastung bevorzugt – aufgrund ihrer Fähigkeit, den Benutzern eine besser vorhersehbare Verwaltung der Transaktionsgebühren zu ermöglichen. Der TransactionType-Wert für diese Transaktionen ist `0x2`. +3. **Typ-2-Transaktionen**, allgemein als EIP-1559-Transaktionen bezeichnet, sind Transaktionen, die mit [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) im Rahmen des [London-Upgrades](/ethereum-forks/#london) von Ethereum eingeführt wurden. Diese haben sich zur Standardform für Transaktionen auf dem Ethereum-Netzwerk entwickelt. Diese Transaktionen führen einen neuen Gebührenmarktmechanismus ein, der durch die Trennung der Transaktionsgebühr in eine Basisgebühr und eine Prioritätsgebühr die Vorhersehbarkeit verbessert. Sie beginnen mit dem Byte `0x02` und enthalten Felder wie `maxPriorityFeePerGas` und `maxFeePerGas`. Typ-2-Transaktionen sind aufgrund ihrer Flexibilität und Effizienz nun der Standard und werden besonders in Zeiten hoher Netzwerkbelastung bevorzugt – aufgrund ihrer Fähigkeit, den Benutzern eine besser vorhersehbare Verwaltung der Transaktionsgebühren zu ermöglichen. Der TransactionType-Wert für diese Transaktionen ist `0x2`. +4. **Typ-3-Transaktionen (Blob)** wurden in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) als Teil des [Dencun-Upgrades](/ethereum-forks/#dencun) von Ethereum eingeführt. Diese Transaktionen sind darauf ausgelegt, „Bloß“-Daten (Binary große Objekte) effizienter zu verarbeiten, was insbesondere Layer-2-Rollups zugutekommt, da sie eine Möglichkeit bieten, Daten zu geringeren Kosten im Ethereum-Netzwerk zu veröffentlichen. Blob-Transaktionen enthalten zusätzliche Felder wie `blobVersionedHashes`, `maxFeePerBlobGas` und `blobGasPrice`. Sie beginnen mit dem Byte `0x03`, und ihr TransactionType-Wert ist `0x3`. Blau-Transaktionen stellen eine erhebliche Verbesserung der Datenverfügbarkeit und Skalierbarkeit von Ethereum dar. +5. **Typ-4-Transaktionen** wurden in [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) als Teil des [Pectra-Upgrades](/roadmap/pectra/) von Ethereum eingeführt. Diese Transaktionen sind so konzipiert, dass sie vorwärtskompatibel mit der Kontoabstraktion sind. Sie ermöglichen es EOAs, sich vorübergehend wie Smart-Contract-Konten zu verhalten, ohne ihre ursprüngliche Funktionalität zu beeinträchtigen. Sie enthalten einen `authorization_list`-Parameter, der den Smart Contract angibt, an den das EOA seine Autorität delegiert. Nach der Transaktion enthält das Code-Feld des EOA die Adresse des delegierten Smart Contracts. -## Weiterführende Informationen {#further-reading} +## Weiterführende Lektüre {#further-reading} -- [EIP-2718: Typisierter Transaktionsumschlag](https://eips.ethereum.org/EIPS/eip-2718) +- [EIP-2718: Typisierter Transaktions-Envelope](https://eips.ethereum.org/EIPS/eip-2718) -_Du kennst Community-Ressourcen die dir geholfen haben? Bearbeite diese Seite und füge sie hinzu!_ +_Sie kennen Community-Resourcen die Ihnen geholfen haben? Bearbeiten Sie diese Seite und fügen Sie sie hinzu!_ ## Verwandte Themen {#related-topics} diff --git a/public/content/translations/de/developers/docs/web2-vs-web3/index.md b/public/content/translations/de/developers/docs/web2-vs-web3/index.md index 92fd6f7abc0..5cdc7f15841 100644 --- a/public/content/translations/de/developers/docs/web2-vs-web3/index.md +++ b/public/content/translations/de/developers/docs/web2-vs-web3/index.md @@ -1,14 +1,14 @@ --- -title: Web2 vs Web3 -description: +title: Web2 vs. Web3 +description: Vergleichen Sie zentralisierte Web2-Dienste mit dezentralen Web3 Anwendungen, die auf der Ethereum-Blockchain-Technologie basieren. lang: de --- Web2 bezieht sich auf die Version des Internets, die die meisten von uns heute kennen. Ein Internet dominiert von Unternehmen, die im Austausch für deine persönlichen Daten Dienstleistungen erbringen. Web3 bezieht sich im Kontext von Ethereum auf dezentrale Apps, die auf der Blockchain laufen. Dies sind Apps, mit denen jeder teilnehmen kann, ohne seine persönlichen Daten zu monetarisieren. -Suchen Sie nach einer Einführung für Einsteiger? Schauen Sie sich unsere [Einführung in web3](/web3/) an. +Suchen Sie nach einer Einführung für Einsteiger? Siehe unsere [Einführung in Web3](/web3/). -## Web3 – Vorteile {#web3-benefits} +## Vorteile von Web3 {#web3-benefits} Viele Web3-Entwickler haben aufgrund der inhärenten Dezentralisierung von Ethereum beschlossen, dApps zu erstellen: @@ -27,7 +27,7 @@ Viele Web3-Entwickler haben aufgrund der inhärenten Dezentralisierung von Ether Das bedeutet nicht, dass alle Dienste in eine dApp umgewandelt werden müssen. Diese Beispiele zeigen die wichtigsten Unterschiede zwischen Web2- und Web3-Diensten. -## Web3 – Einschränkungen {#web3-limitations} +## Einschränkungen von Web3 {#web3-limitations} Web3 hat momentan einige Einschränkungen: @@ -40,23 +40,23 @@ Web3 hat momentan einige Einschränkungen: In der unten stehenden Tabelle finden Sie einige Vor- und Nachteile zentralisierter und dezentralisierter digitaler Netzwerke. -| Zentralisierte Systeme | Dezentralisierte Systeme | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Niedriger Netzwerkdurchmesser (alle Teilnehmer sind mit einer zentralen Behörde verbunden); Informationen werden schnell verbreitet, da die Verbreitung durch eine zentrale Autorität mit vielen rechnerischen Ressourcen gehandhabt wird. | Die am weitesten entfernten Teilnehmer im Netzwerk können möglicherweise viele Abstände voneinander entfernt sein. Von einer Seite des Netzwerks ausgestrahlte Informationen können lange brauchen, bis sie die andere Seite erreichen. | -| In der Regel leistungsfähiger (höherer Durchsatz, weniger gesamte Rechenressourcen nötig) und leichter implementierbar. | In der Regel niedrigere Leistung (niedrigerer Durchsatz, mehr gesamte Rechenressourcen nötig) und komplexer zu implementieren. | -| Im Falle widersprüchlicher Daten ist die Auflösung klar und einfach: Die ultimative Quelle der Wahrheit ist die zentrale Autorität. | Ein Protokoll (oft komplex) wird für die Streitbeilegung benötigt , wenn Peers widersprüchliche Ansprüche auf den Zustand der Daten erheben, auf die die Teilnehmer synchronisiert werden sollen. | -| Single Point of Failure: Böswillige Akteure können das Netzwerk möglicherweise durch gezielte Angriffe auf die zentrale Autorität ausschalten. | No Single Point of Failure: Das Netzwerk kann immer noch funktionieren, auch wenn ein großer Teil der Teilnehmer angegriffen bzw. ausgeschaltet wird. | +| Zentralisierte Systeme | Dezentralisierte Systeme | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Niedriger Netzwerkdurchmesser (alle Teilnehmer sind mit einer zentralen Behörde verbunden); Informationen werden schnell verbreitet, da die Verbreitung durch eine zentrale Autorität mit vielen rechnerischen Ressourcen gehandhabt wird. | Die am weitesten entfernten Teilnehmer im Netzwerk können möglicherweise viele Abstände voneinander entfernt sein. Von einer Seite des Netzwerks ausgestrahlte Informationen können lange brauchen, bis sie die andere Seite erreichen. | +| In der Regel leistungsfähiger (höherer Durchsatz, weniger gesamte Rechenressourcen nötig) und leichter implementierbar. | In der Regel niedrigere Leistung (niedrigerer Durchsatz, mehr gesamte Rechenressourcen nötig) und komplexer zu implementieren. | +| Im Falle widersprüchlicher Daten ist die Auflösung klar und einfach: Die ultimative Quelle der Wahrheit ist die zentrale Autorität. | Ein Protokoll (oft komplex) wird für die Streitbeilegung benötigt , wenn Peers widersprüchliche Ansprüche auf den Zustand der Daten erheben, auf die die Teilnehmer synchronisiert werden sollen. | +| Single Point of Failure: Böswillige Akteure können das Netzwerk möglicherweise durch gezielte Angriffe auf die zentrale Autorität ausschalten. | No Single Point of Failure: Das Netzwerk kann immer noch funktionieren, auch wenn ein großer Teil der Teilnehmer angegriffen bzw. ausgeschaltet wird. | | Die Koordination zwischen den Netzwerkteilnehmern ist viel einfacher und wird von einer zentralen Autorität verwaltet. Die zentrale Autorität kann Netzwerkteilnehmer dazu zwingen, Upgrades, Protokoll-Updates etc. mit sehr wenig Widerstand zu übernehmen. | Die Koordinierung ist oft schwierig, da kein einziger Agent das letzte Wort bei Entscheidungen auf Netzwerkebene, Protokoll-Upgrades usw. hat. Im schlimmsten Fall neigt das Netzwerk zur Zersplitterung, wenn es Meinungsverschiedenheiten über Änderungen des Protokolls gibt. | -| Die zentrale Autorität kann Daten zensieren, wodurch Teile des Netzwerks von der Interaktion mit dem Rest des Netzes abgeschnitten werden könnten. | Zensur ist viel schwieriger, da Informationen viele Möglichkeiten haben, sich über das Netzwerk zu verbreiten. | -| Die Teilnahme am Netzwerk wird von der zentralen Autorität kontrolliert. | Jeder kann am Netzwerk teilnehmen; es gibt keine „Gatekeeper“. Idealerweise sind die Teilnahmekosten sehr niedrig. | +| Die zentrale Autorität kann Daten zensieren, wodurch Teile des Netzwerks von der Interaktion mit dem Rest des Netzes abgeschnitten werden könnten. | Zensur ist viel schwieriger, da Informationen viele Möglichkeiten haben, sich über das Netzwerk zu verbreiten. | +| Die Teilnahme am Netzwerk wird von der zentralen Autorität kontrolliert. | Jeder kann am Netzwerk teilnehmen; es gibt keine „Gatekeeper“. Idealerweise sind die Teilnahmekosten sehr niedrig. | Beachten Sie, dass das allgemeine Muster sind, die nicht auf jedes Netzwerk zutreffen. In der Realität liegt der Grad der Zentralisierung bzw. Dezentralisierung eines Netzwerks in einem Spektrum. Kein Netzwerk ist vollständig zentralisiert oder vollständig dezentralisiert. -## Weiterführende Informationen {#further-reading} +## Weiterführende Lektüre {#further-reading} -- [Was ist Web3?](/web3/) – _ethereum.org_ -- [Die Architektur einer Web 3.0 Anwendung](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) – _Preethi Kasireddy_ -- [Die Bedeutung der Dezentralisierung](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _Feb 6, 2017 – Vitalik Buterin_ -- [Why Decentralization Matters](https://medium.com/s/story/why-decentralization-matters-5e3f79f7638e) _Feb 18, 2018 – Chris Dixon_ -- [Was ist Web 3.0 & Warum es wichtig ist](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _Dez 31, 2019 – Max Mersch und Richard Muirhead_ -- [Warum wir Web 3.0 brauchen](https://medium.com/@gavofyork/why-we-need-web-3-0-5da4f2bf95ab)_Sep 12,2018 - Gavin Wood_ +- [Was ist Web3?](/web3/) - _ethereum.org_ +- [Die Architektur einer Web 3.0-Anwendung](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) - _Preethi Kasireddy_ +- [Die Bedeutung von Dezentralisierung](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _6. Feb. 2017 - Vitalik Buterin_ +- [Warum Dezentralisierung wichtig ist](https://onezero.medium.com/why-decentralization-matters-5e3f79f7638e) _18. Feb. 2018 - Chris Dixon_ +- [Was ist Web 3.0 und warum ist es wichtig](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _31. Dez. 2019 - Max Mersch und Richard Muirhead_ +- [Warum wir Web 3.0 brauchen](https://gavofyork.medium.com/why-we-need-web-3-0-5da4f2bf95ab) _12. Sep. 2018 - Gavin Wood_ diff --git a/public/content/translations/de/developers/docs/wrapped-eth/index.md b/public/content/translations/de/developers/docs/wrapped-eth/index.md index 2b6099003a0..90a0439dc4e 100644 --- a/public/content/translations/de/developers/docs/wrapped-eth/index.md +++ b/public/content/translations/de/developers/docs/wrapped-eth/index.md @@ -1,6 +1,6 @@ --- -title: Was ist „Wrapped Ether“ (WETH) -description: Eine Einführung in Wrapped Ether (WETH) – ein ERC20-kompatibler Wrapper für Ether (ETH). +title: "Was ist „Wrapped Ether“ (WETH)" +description: "Eine Einführung in Wrapped Ether (WETH) – ein ERC20-kompatibler Wrapper für Ether (ETH)." lang: de --- @@ -35,19 +35,16 @@ Sie können WETH in ETH umwandeln, indem Sie den WETH-Smart Contract verwenden. Sie zahlen Gasgebühren, um ETH mit dem WETH-Vertrag zu verpacken oder zu entpacken. - WETH gilt allgemein als sicher, da es auf einem einfachen, bewährten Smart Contract basiert. Der WETH-Vertrag wurde zudem formal verifiziert, was den höchsten Sicherheitsstandard für Smart Contracts auf Ethereum darstellt. - Neben der [kanonischen Implementierung von WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2), die auf dieser Seite beschrieben ist, gibt es auch andere Varianten. Diese können benutzerdefinierte Token sein, die von App-Entwicklern erstellt wurden, oder Versionen, die auf anderen Blockchains herausgegeben wurden, und sich unterschiedlich verhalten oder unterschiedliche Sicherheitseigenschaften haben. **Überprüfen Sie immer die Token-Informationen, um zu erfahren, mit welcher WETH-Implementierung Sie interagieren.** - @@ -55,7 +52,6 @@ Neben der [kanonischen Implementierung von WETH](https://etherscan.io/token/0xc0 - [Ethereum-Mainnet](https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) - [Arbitrum](https://arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1) - [Optimism](https://optimistic.etherscan.io/token/0x4200000000000000000000000000000000000006) - ## Weiterführende Lektüre {#further-reading} diff --git a/public/content/translations/de/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md b/public/content/translations/de/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md index 9d19e536037..3c6106303de 100644 --- a/public/content/translations/de/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md +++ b/public/content/translations/de/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md @@ -1,123 +1,122 @@ --- -title: Ethereum-Einführung für Pythonentwickler, Teil 1 -description: Eine Einführung in die Entwicklung von Ethereum, zugeschnitten auf Entwickler mit einem Hintergrund in Python +title: "Ethereum-Einführung für Python-Entwickler, Teil 1" +description: "Eine Einführung in die Ethereum-Entwicklung, besonders nützlich für Personen mit Kenntnissen in der Programmiersprache Python" author: Marc Garreau lang: de -tags: - - "Erste Schritte" - - "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/ --- -Sie haben bereits von Ethereum gehört und möchten tiefer in die Materie eintauchen? In diesem Beitrag werden einige Blockchain-Grundlagen kurz erläutert und dann werden Sie mit einem simulierten Ethereum-Node interagieren – Blockdaten lesen, Kontostände prüfen und Transaktionen senden. Dabei werden wir die Unterschiede zwischen den traditionellen Methoden der App-Entwicklung und diesem neuen dezentralen Modell herausstellen. +Sie haben also von dieser Ethereum-Sache gehört und sind bereit, sich in den Kaninchenbau zu wagen? Dieser Beitrag behandelt kurz einige Blockchain-Grundlagen und führt Sie dann in die Interaktion mit einem simulierten Ethereum-Node ein – das Lesen von Blockdaten, die Überprüfung von Kontoständen und das Senden von Transaktionen. Dabei werden wir die Unterschiede zwischen traditionellen Methoden zur Erstellung von Apps und diesem neuen dezentralen Paradigma hervorheben. -## (Benötigtes) Vorwissen {#soft-prerequisites} +## (Optionale) Voraussetzungen {#soft-prerequisites} -Dieser Beitrag ist für Entwickler mit den unterschiedlichsten Kenntnissen gedacht. Zum Einsatz kommen [Python-Tools](/developers/docs/programming-languages/python/). Diese dienen allerdings nur als Mittel zum Zweck, wenn Sie also keine Vorerfahrung mit Python mitbringen, ist das kein Problem. Allerdings treffe ich ein paar Annahmen über Ihr Vorwissen, damit wir schnell zu den Ethereum-spezifischen Inhalten übergehen können. +Dieser Beitrag soll für eine breite Palette von Entwicklern zugänglich sein. [Python-Tools](/developers/docs/programming-languages/python/) werden verwendet, aber sie sind nur ein Mittel zum Zweck – es ist kein Problem, wenn Sie kein Python-Entwickler sind. Ich werde jedoch ein paar Annahmen darüber treffen, was Sie bereits wissen, damit wir schnell zu den Ethereum-spezifischen Teilen übergehen können. -Vorbedinungen: +Annahmen: -- Sie können sich in einem Terminal bewegen -- Sie haben bereits ein paar Zeilen Python-Code geschrieben -- Python Version 3.6 oder höher ist auf Ihrem Rechner installiert (die Verwendung einer [virtuellen Umgebung](https://realpython.com/effective-python-environment/#virtual-environments) wird dringend empfohlen) -- Sie haben `pip`, das Python-Installationspaket, installiert (Nochmals: Sollten Sie einige dieser Anforderungen nicht erfüllen oder nicht nebenher mit programmieren wollen, sollte es dennoch möglich sein, den Beispielen inhaltlich zu folgen). +- Sie können sich in einem Terminal zurechtfinden, +- Sie haben ein paar Zeilen Python-Code geschrieben, +- Python Version 3.6 oder höher ist auf Ihrem Rechner installiert (die Verwendung einer [virtuellen Umgebung](https://realpython.com/effective-python-environment/#virtual-environments) wird dringend empfohlen), und +- Sie haben `pip`, den Paket-Installer von Python, verwendet. + Auch wenn einige dieser Punkte nicht zutreffen oder Sie nicht vorhaben, den Code in diesem Artikel zu reproduzieren, können Sie dem Inhalt wahrscheinlich trotzdem gut folgen. -## Blockchains, kurz gefasst {#blockchains-briefly} +## Blockchains, kurz erklärt {#blockchains-briefly} -Es gibt viele Möglichkeiten, Ethereum zu beschreiben, doch im Kern ist es eine Blockchain. Blockchains bestehen aus einer Reihe von Blöcken, also lassen Sie uns damit beginnen. Einfach ausgedrückt: Jeder Block der Ethereum-Blockchain besteht nur aus einigen Metadaten und einer Liste von Transaktionen. Im JSON-Format sieht das folgendermaßen aus: +Es gibt viele Möglichkeiten, Ethereum zu beschreiben, doch im Kern ist es eine Blockchain. Blockchains bestehen aus einer Reihe von Blöcken, also lassen Sie uns damit beginnen. Einfach ausgedrückt, ist jeder Block in der Ethereum-Blockchain nur eine Ansammlung von Metadaten und eine Liste von Transaktionen. Im JSON-Format sieht das in etwa so aus: ```json { "number": 1234567, "hash": "0xabc123...", "parentHash": "0xdef456...", - "miner": "0xa1b2c3...", ..., "transactions": [...] } ``` -Jeder [Block](/developers/docs/blocks/) hat eine Referenz auf den vor ihm liegenden Block. Der `parentHash` ist einfach der Hash des vorherigen Blocks. +Jeder [Block](/developers/docs/blocks/) hat eine Referenz auf den Block, der davor kam; der `parentHash` ist einfach der Hash des vorherigen Blocks. -Hinweis: Ethereum nutzt regelmäßig Hashfunktionen, um Werte mit fester Länge (Hashes) zu erzeugen. Hashes spielen eine wichtige Rolle in Ethereum, für den Einstieg können Sie sie einfach als eindeutige ID vorstellen. +Hinweis: Ethereum verwendet regelmäßig Hash-Funktionen, um Werte fester Größe („Hashes“) zu erzeugen. Hashes spielen eine wichtige Rolle in Ethereum, aber vorerst können Sie sie sich einfach als eindeutige IDs vorstellen. ![Ein Diagramm, das eine Blockchain einschließlich der Daten in jedem Block darstellt](./blockchain-diagram.png) -_Eine Blockchain ist im Grunde eine verknüpfte Liste. Jeder Block verweist auf den vorangehenden Block._ +_Eine Blockchain ist im Grunde eine verkettete Liste; jeder Block hat eine Referenz auf den vorherigen Block._ -Diese Datenstruktur ist nicht neu. Aber die Regeln (z. B. Peer-to-Peer-Protokolle), die im Netzwerk gelten, sind neu. Es gibt keine zentrale Autorität. Die Netzwerkteilnehmer (Peers) müssen zusammenarbeiten und konkurrieren, um das Netzwerk aufrechtzuerhalten und zu entscheiden, welche Transaktionen in den nächsten Block aufgenommen werden. Wenn Sie also einem Freund Geld schicken möchten, müssen Sie diese Transaktion an das Netzwerk senden und dann darauf warten, dass sie in einen kommenden Block aufgenommen wird. +Diese Datenstruktur ist nichts Neues, aber die Regeln (d. h. die Peer-to-Peer-Protokolle), die das Netzwerk steuern, sind es. Es gibt keine zentrale Autorität; das Netzwerk von Peers muss zusammenarbeiten, um das Netzwerk aufrechtzuerhalten, und konkurrieren, um zu entscheiden, welche Transaktionen in den nächsten Block aufgenommen werden. Wenn Sie also einem Freund Geld schicken möchten, müssen Sie diese Transaktion an das Netzwerk senden und dann darauf warten, dass sie in einen kommenden Block aufgenommen wird. -Die einzige Möglichkeit für die Blockchain, zu verifizieren, dass Geld wirklich von einem Nutzer zu einem anderen gesendet wurde, ist die Nutzung einer für diese Blockchain nativen Währung (d. h. die von ihr geschaffen und verwaltet wird). Bei Ethereum heißt diese Währung Ether. Die Ethereum-Blockchain enthält die einzigen offiziellen Aufzeichnungen über die Kontostände. +Die einzige Möglichkeit für die Blockchain, zu verifizieren, dass Geld wirklich von einem Nutzer zu einem anderen gesendet wurde, ist die Nutzung einer für diese Blockchain nativen Währung (d. h. die von ihr geschaffen und verwaltet wird). In Ethereum heißt diese Währung Ether, und die Ethereum-Blockchain enthält die einzige offizielle Aufzeichnung der Kontostände. -## Ein neues Modell {#a-new-paradigm} +## Ein neues Paradigma {#a-new-paradigm} -Dieser neue dezentralisierte Technologie-Stack hat neue Entwicklertools hervorgebracht. Solche Tools gibt es in vielen Programmiersprachen, aber in diesem Beitrag schauen wir durch die Python-Brille. Um es noch einmal zu betonen: Selbst wenn Python nicht Ihre bevorzugte Sprache ist, sollten Sie den Anweisungen trotzdem leicht folgen können. +Dieser neue dezentralisierte Technologie-Stack hat neue Entwicklertools hervorgebracht. Solche Tools gibt es in vielen Programmiersprachen, aber wir werden sie aus der Python-Perspektive betrachten. Um es noch einmal zu wiederholen: Auch wenn Python nicht Ihre bevorzugte Sprache ist, sollte es kein Problem sein, mitzukommen. -Python-Entwickler, die mit Ethereum interagieren möchten, nutzen wahrscheinlich [Web3.py](https://web3py.readthedocs.io/). Web3.py ist eine Bibliothek, die es sehr einfach macht, sich mit einem Ethereum-Node zu verbinden und dann Daten zu senden und zu empfangen. +Python-Entwickler, die mit Ethereum interagieren möchten, greifen wahrscheinlich zu [Web3.py](https://web3py.readthedocs.io/). Web3.py ist eine Bibliothek, die die Verbindung zu einem Ethereum-Node sowie das Senden und Empfangen von Daten von diesem erheblich vereinfacht. -Hinweis: "Ethereum-Node" und "Ethereum-Client" werden synonym verwendet. In beiden Fällen ist Software gemeint, die ein Teilnehmer am Ethereum-Netzwerk ausführt. Diese Software kann Blockdaten lesen, Updates empfangen, wenn neue Blöcke zur Kette hinzugefügt ("geminted") werden, neue Transaktionen übertragen und vieles mehr. +Hinweis: „Ethereum-Node“ und „Ethereum-Client“ werden synonym verwendet. In beiden Fällen bezieht es sich auf die Software, die ein Teilnehmer im Ethereum-Netzwerk ausführt. Diese Software kann Blockdaten lesen, Aktualisierungen empfangen, wenn neue Blöcke zur Kette hinzugefügt werden, neue Transaktionen übertragen und vieles mehr. Technisch gesehen ist der Client die Software, der Node ist der Computer, auf dem die Software läuft. -[Ethereum-Clients](/developers/docs/nodes-and-clients/) können so konfiguriert werden, dass sie über [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP oder Websockets erreichbar sind. Daher muss Web3.py diese Konfiguration spiegeln. Web3.py bezeichnet diese Verbindungsoptionen als **Anbieter**. Sie müssen einen der drei Anbieter wählen, um eine Web3.py-Instanz mit Ihrem Node zu verbinden. +[Ethereum-Clients](/developers/docs/nodes-and-clients/) können so konfiguriert werden, dass sie über [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP oder Websockets erreichbar sind, daher muss Web3.py diese Konfiguration widerspiegeln. Web3.py bezeichnet diese Verbindungsoptionen als **Anbieter**. Sie sollten einen der drei Anbieter wählen, um die Web3.py-Instanz mit Ihrem Node zu verbinden. ![Ein Diagramm, das zeigt, wie web3.py IPC verwendet, um Ihre Anwendung mit einem Ethereum-Node zu verbinden](./web3py-and-nodes.png) -_Konfigurieren Sie den Ethereum-Node und Web3.py so, dass sie über das gleiche Protokoll kommunizieren. Im folgenden Diagramm ist das beispielsweise IPC._ +_Konfigurieren Sie den Ethereum-Node und Web3.py so, dass sie über dasselbe Protokoll kommunizieren, z. B. IPC in diesem Diagramm._ -Sobald Web3.py richtig konfiguriert ist, können Sie mit der Blockchain interagieren. Hier sind eine paar Anwendungsbeispiele für Web3.py als Vorschau auf das was noch folgt: +Sobald Web3.py richtig konfiguriert ist, können Sie mit der Blockchain interagieren. Hier sind ein paar Anwendungsbeispiele für Web3.py als Vorschau auf das, was noch folgt: ```python -# read block data: +# Blockdaten lesen: w3.eth.get_block('latest') -# send a transaction: +# eine Transaktion senden: w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) ``` ## Installation {#installation} -In dieser Einführung arbeiten wir nur mit dem Python-Interpreter. Wir erzeugen keine Verzeichnisse, Dateien, Klassen oder Funktionen. +In dieser exemplarischen Vorgehensweise arbeiten wir nur innerhalb eines Python-Interpreters. Wir werden keine Verzeichnisse, Dateien, Klassen oder Funktionen erstellen. -Hinweis: In den folgenden Beispielen kennzeichnet '$' Befehle, die im Terminal ausgeführt werden. (Geben Sie dabei das '$' nicht mit ein. Es ist nur ein Hinweis auf den Beginn einer Zeile.) +Hinweis: In den folgenden Beispielen sind Befehle, die mit `$` beginnen, für die Ausführung im Terminal vorgesehen. (Geben Sie das `$` nicht mit ein, es kennzeichnet nur den Zeilenanfang.) -Installieren Sie zuerst [IPython](https://ipython.org/), um eine benutzerfreundliche Umgebung zu erstellen. IPython bietet neben anderen Funktionen eine Tab-Vervollständigung an. Damit lässt sich einfacher heruasfinden, was innerhalb von Web3.py möglich ist. +Installieren Sie zuerst [IPython](https://ipython.org/), um eine benutzerfreundliche Umgebung zum Erkunden zu erhalten. IPython bietet unter anderem eine Tab-Vervollständigung an, die es viel einfacher macht, zu erkennen, was mit Web3.py alles möglich ist. ```bash -$ pip install ipython +pip install ipython ``` -Web3.py wird unter dem Namen `web3` publiziert. Nehmen Sie die Installation wie folgt vor: +Web3.py wird unter dem Namen `web3` veröffentlicht. Installieren Sie es wie folgt: ```bash -$ pip install web3 +pip install web3 ``` -Als Nächstes werden wir eine Blockchain-Simulation durchführen, die noch einige weitere Abhängigkeiten erfordert. Nehmen Sie die Installation wie folgt vor: +Noch etwas – wir werden später eine Blockchain simulieren, was ein paar weitere Abhängigkeiten erfordert. Sie können diese wie folgt installieren: ```bash -$ pip install 'web3[tester]' +pip install 'web3[tester]' ``` -Nun kann es losgehen. +Jetzt sind Sie startklar! + +Hinweis: Das `web3[tester]`-Paket funktioniert bis Python 3.10.xx ## Eine Sandbox einrichten {#spin-up-a-sandbox} -Öffnen Sie eine neue Python-Umgebung, indem Sie `ipython` in Ihrem Terminal ausführen. Diese Ausführung ist vergleichbar mit `python`, bietet aber deutlich mehr Funktionen. +Öffnen Sie eine neue Python-Umgebung, indem Sie `ipython` in Ihrem Terminal ausführen. Dies ist vergleichbar mit der Ausführung von `python`, bietet aber deutlich mehr Funktionen. ```bash -$ ipython +ipython ``` -Es werden einige Informationen über Ihre aktuelle Version von Python und IPython angezeigt. Anschließend sollten Sie eine Aufforderung zur Eingabe erhalten: +Es werden einige Informationen über die Versionen von Python und IPython angezeigt, die Sie verwenden. Anschließend sollten Sie eine Aufforderung zur Eingabe sehen: ```python In [1]: ``` -Sie befinden sich jetzt in einer interaktiven Python-Shell-Umgebung. Hier können Sie sich nun austoben. Nun ist es an der Zeit, Web3.py zu importieren: +Sie sehen nun eine interaktive Python-Shell. Im Grunde ist es eine Sandbox zum Herumspielen. Wenn Sie es bis hierher geschafft haben, ist es an der Zeit, Web3.py zu importieren: ```python In [1]: from web3 import Web3 @@ -125,74 +124,75 @@ In [1]: from web3 import Web3 ## Einführung in das Web3-Modul {#introducing-the-web3-module} -Das [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api)-Modul ist nicht nur ein Gateway zu Ethereum, sondern bietet auch einige komfortable Funktionen an. Sehen wir uns ein paar davon genauer an. +Das [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api)-Modul ist nicht nur ein Gateway zu Ethereum, sondern bietet auch einige praktische Funktionen. Sehen wir uns ein paar davon an. -In einer Ethereum-Anwendung müssen Sie üblicherweise Währungsbezeichnungen umrechnen. Das Web3-Modul stellt Ihnen dazu hilfreiche Methoden zur Verfügung: [fromWei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.fromWei) und [toWei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toWei). +In einer Ethereum-Anwendung müssen Sie üblicherweise Währungsbezeichnungen umrechnen. Das Web3-Modul bietet hierfür ein paar Hilfsmethoden: [from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) und [to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei). -Hinweis: Computer sind bekanntermaßen schlecht bei der Verarbeitung von Dezimalmathematik. Um das zu umgehen, geben Entwickler Dollarbeträge oft in Cent an. Beispiel: Ein Artikel mit einem Preis von 5,99 USD wird in der Datenbank als 599 angelegt. +Hinweis: Computer sind bekanntermaßen schlecht bei der Verarbeitung von Dezimalzahlen. Um dies zu umgehen, speichern Entwickler Dollarbeträge oft in Cent. Beispielsweise kann ein Artikel mit einem Preis von 5,99 $ in der Datenbank als 599 gespeichert werden. -Transaktionen in Ether werden in ähnlicher Weise verwaltet. Aber statt zwei Dezimalstellen hat Ether 18 Stück. Die kleinste Einheit von Ether wird Wei genannt. Das ist auch der Wert, der beim Senden von Transaktionen angegeben wird. +Ein ähnliches Muster wird beim Umgang mit Transaktionen in Ether verwendet. Doch anstelle von zwei Dezimalstellen hat Ether 18! Die kleinste Einheit von Ether wird Wei genannt. Das ist also der Wert, der beim Senden von Transaktionen angegeben wird. -1 Ether = 1000000000000000000 Wei +1 Ether = 1.000.000.000.000.000.000 Wei -1 Wei = 0.000000000000000001 Ether +1 Wei = 0,000000000000000001 Ether -Versuchen Sie, einige Werte nach und von Wei zu konvertieren. [Beachten Sie](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations), dass es zwischen Ether und Wei noch andere Einheiten gibt. Eine der bekanntesten ist **Gwei**, da Transaktionsgebühren in dieser Einheit angegeben werden. +Versuchen Sie, einige Werte von und in Wei umzurechnen. Beachten Sie, dass es [Namen für viele der Denominationen](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations) zwischen Ether und Wei gibt. Eine der bekanntesten davon ist **Gwei**, da Transaktionsgebühren oft in dieser Einheit dargestellt werden. ```python -In [2]: Web3.toWei(1, 'ether') +In [2]: Web3.to_wei(1, 'ether') Out[2]: 1000000000000000000 -In [3]: Web3.fromWei(500000000, 'gwei') +In [3]: Web3.from_wei(500000000, 'gwei') Out[3]: Decimal('0.5') ``` -Das Web3-Modul enthält außerdem einen Datenformatkonvertierer (z. B. [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), Adresse (z. B., [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)), und Hashfunktionen (z. B., [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak)). Viele davon werden hier genauer erklärt. Um alle verfügbaren Funktionen und Eigenschaften anzuzeigen, können Sie die Autovervollständigung in IPython nutzen. Geben Sie dafür den folgenden Code ein: `Web3`. Anschließend drücken Sie bitte zweimal die Tab-Taste. +Weitere Hilfsmethoden im Web3-Modul umfassen Datenformat-Konverter (z. B. [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), Adress-Hilfsfunktionen (z. B. [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)) und Hash-Funktionen (z. B. [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak)). Viele davon werden später in dieser Reihe behandelt. Um alle verfügbaren Methoden und Eigenschaften anzuzeigen, nutzen Sie die automatische Vervollständigung von IPython, indem Sie `Web3` eingeben. und nach dem Punkt zweimal die Tab-Taste drücken. -## Kommunikation mit der Blockchain {#talk-to-the-chain} +## Kommunikation mit der Chain {#talk-to-the-chain} -Die bisher vorgestellten Funktionen sind toll. Aber sehen wir uns nun einmal die Blockchain genauer an. Im nächsten Schritt konfiguireren wir Web3.py für die Kommunikation mit einem Ethereum-Node. Dabei können wir IPC, HTTP oder Websocket-Anbieter verwenden. +Die Hilfsmethoden sind schön und gut, aber lassen Sie uns zur Blockchain übergehen. Der nächste Schritt ist die Konfiguration von Web3.py zur Kommunikation mit einem Ethereum-Node. Hier haben wir die Möglichkeit, die Anbieter IPC, HTTP oder Websocket zu verwenden. -Wir werden hier nicht weiter darauf eingehen, aber ein Beispiel für einen kompletten Workflow mit dem HTTP-Provider könnte wie folgt aussehen: +Wir werden diesen Weg nicht weiter verfolgen, aber ein Beispiel für einen kompletten Arbeitsablauf mit dem HTTP-Anbieter könnte etwa so aussehen: - Laden Sie einen Ethereum-Node herunter, z. B. [Geth](https://geth.ethereum.org/). -- Starten Sie Geth in einem Terminalfenster und warten Sie bis die Netzwerksynchronisierung abgeschlossen ist. Der Standard-HTTP-Port ist `8545`, kann jedoch umkonfiguriert werden. -- Stellen Sie eine Verbindung von Web3.py zu dem Ethereum-Node über HTTP, `localhost:8545`, her. `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` -- Agieren Sie über die `w3`-Instanz mit dem Node. +- Starten Sie Geth in einem Terminalfenster und warten Sie, bis es sich mit dem Netzwerk synchronisiert hat. Der Standard-HTTP-Port ist `8545`, ist aber konfigurierbar. +- Weisen Sie Web3.py an, sich mit dem Node über HTTP auf `localhost:8545` zu verbinden. + `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- Verwenden Sie die `w3`-Instanz, um mit dem Node zu interagieren. -Wenn Sie nur an einer Entwicklungsumgebung interessiert sind, ist dieser Weg unnötig, da der Synchronisierungsprozess mehrere Stunden dauert. Web3.py stellt für diesen Zweck einen vierten Provider zur Verfügung, den **EthereumTesterProvider**. Dieser Test-Provider ist mit einem simulierten Ethereum-Node mit Fake-Währungen und einfachen Berechtigungen verknüpft. +Obwohl dies ein „echter“ Weg ist, dauert der Synchronisierungsprozess Stunden und ist unnötig, wenn Sie nur eine Entwicklungsumgebung wollen. Web3.py stellt für diesen Zweck einen vierten Anbieter bereit, den **EthereumTesterProvider**. Dieser Testanbieter verknüpft sich mit einem simulierten Ethereum-Node mit gelockerten Berechtigungen und einer Spielwährung. ![Ein Diagramm, das den EthereumTesterProvider zeigt, der Ihre web3.py-Anwendung mit einem simulierten Ethereum-Node verbindet](./ethereumtesterprovider.png) _Der EthereumTesterProvider verbindet sich mit einem simulierten Node und ist praktisch für schnelle Entwicklungsumgebungen._ -Dieser simulierter Node heißt [eth-tester](https://github.com/ethereum/eth-tester) und wurde mit dem Befehl `pip install web3[tester]` installiert. Um das Web3.py mit dem Testanbieter zu verbinden, geben Sie Folgendes ein: +Dieser simulierte Node heißt [eth-tester](https://github.com/ethereum/eth-tester) und wurde als Teil des `pip install web3[tester]`-Befehls installiert. Die Konfiguration von Web3.py zur Verwendung dieses Testanbieters ist so einfach wie: ```python In [4]: w3 = Web3(Web3.EthereumTesterProvider()) ``` -Jetzt können Sie mit der Blockchain kommunizieren. Wie das genau funktioniert? Ich habe da etwas für Sie zusammengestellt. Machen wir eine schnelle Tour. +Jetzt sind Sie bereit, auf der Chain zu surfen! Das ist kein gängiger Ausdruck. Das habe ich mir gerade ausgedacht. Machen wir eine kurze Tour. -## Los geht's {#the-quick-tour} +## Die Kurztour {#the-quick-tour} -Zuallererst eine Zuverlässigkeitsüberprüfung: +Zuallererst ein Sanity-Check: ```python -In [5]: w3.isConnected() +In [5]: w3.is_connected() Out[5]: True ``` -Da wir einen Testanbieter verwenden, ist der Test nicht sehr aussagekräftig, doch falls er fehlschlägt, ist die Wahrscheinlichkeit hoch, dass Sie eine falsche Variable in `w3` eingegeben haben. Überprüfen Sie, ob Sie die inneren Klammern eingefügt haben, also `Web3.EthereumTesterProvider()`. +Da wir den Testanbieter verwenden, ist dies kein sehr aussagekräftiger Test, aber wenn er fehlschlägt, haben Sie wahrscheinlich bei der Instanziierung der `w3`-Variable etwas falsch eingegeben. Überprüfen Sie, ob Sie die inneren Klammern eingefügt haben, d. h. `Web3.EthereumTesterProvider()`. -## 1. Stop auf der Tour: [Konten](/developers/docs/accounts/) {#tour-stop-1-accounts} +## Tour-Stopp Nr. 1: [Konten](/developers/docs/accounts/) {#tour-stop-1-accounts} -Der Einfachheit halber hat der Testanbieter bereits einige Konten eingerichtet und sie mit Test-Ether gefüllt. +Der Einfachheit halber hat der Testanbieter einige Konten erstellt und sie mit Test-Ether vorgeladen. -Zunächst sehen wir uns die folgende Kontenliste an: +Zuerst sehen wir uns eine Liste dieser Konten an: ```python In [6]: w3.eth.accounts @@ -201,27 +201,27 @@ Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] ``` -Wenn Sie diesen Befehl ausführen, sollten Sie eine Liste von zehn Zeichenketten sehen, die mit `0x` beginnen. Jede davon ist eine **öffentliche Adresse** und gewissermaßen analog zur Kontonummer auf einem Prüfkonto. Diese Adresse würden Sie angeben, wenn Ihnen jemand Ether senden will. +Wenn Sie diesen Befehl ausführen, sollten Sie eine Liste von zehn Zeichenketten sehen, die mit `0x` beginnen. Jede davon ist eine **öffentliche Adresse** und ist in gewisser Weise analog zur Kontonummer eines Girokontos. Sie würden diese Adresse jemandem geben, der Ihnen Ether senden möchte. -Wie bereits erwähnt, hat der Testanbieter jedes dieser Konten mit Test-Ether gefüllt. Lassen Sie uns herausfinden, wie viel sich auf dem ersten Konto befindet: +Wie bereits erwähnt, hat der Testanbieter jedes dieser Konten mit etwas Test-Ether vorgeladen. Finden wir heraus, wie viel sich auf dem ersten Konto befindet: ```python In [7]: w3.eth.get_balance(w3.eth.accounts[0]) Out[7]: 1000000000000000000000000 ``` -Das sind viele Nullen. Bevor Sie vor Freude in die Luft springen, erinnern Sie sich bitte an unsere vorherige Lektion über die Schreibweise von Währungen. Ether wird in der kleinsten Einheit Wei angegeben. Rechnen Sie dies in Ether um: +Das sind eine Menge Nullen! Bevor Sie lachend zur falschen Bank gehen, erinnern Sie sich an die Lektion über Währungsbezeichnungen von vorhin. Ether-Werte werden in der kleinsten Einheit, Wei, dargestellt. Rechnen Sie das in Ether um: ```python -In [8]: w3.fromWei(1000000000000000000000000, 'ether') +In [8]: w3.from_wei(1000000000000000000000000, 'ether') Out[8]: Decimal('1000000') ``` -Eine Millionen Test-Ether – sind immer noch gut. +Eine Million Test-Ether – immer noch nicht zu verachten. -## 2. Stop auf der Tour: Blockdaten {#tour-stop-2-block-data} +## Tour-Stopp Nr. 2: Blockdaten {#tour-stop-2-block-data} -Werfen wir einen Blick auf den Status dieser simulierten Blockchain: +Werfen wir einen Blick auf den Zustand dieser simulierten Blockchain: ```python In [9]: w3.eth.get_block('latest') @@ -234,32 +234,35 @@ Out[9]: AttributeDict({ }) ``` -Über einen Block werden viele Informationen zurückgegeben, doch auf ein paar davon sollten Sie achten: +Über einen Block werden viele Informationen zurückgegeben, aber hier sind nur ein paar Dinge hervorzuheben: -- Die Blocknummer ist Null – unabhängig davon, wie lange es her ist, dass Sie den Testanbieter konfiguriert haben. Im Gegensatz zum echten Ethereum-Netzwerk, das ungefähr alle 15 Sekunden einen neuen Block erstellt, wartet diese Simulation, bis sie von Ihnen etwas zu tun bekommt. -- Die `transactions` sind ebenfalls leer, da wir bisher noch nichts gemacht haben. Dieser erste Block ist ein **leerer Block**, nur um die Kette in Gang zu setzen. -- Beachten Sie, dass der `parentHash` nur ein Bund aus leeren Bytes ist. Das bedeutet, dass es sich um den ersten Block in der Kette handelt, auch bekannt als **Genesis Block**. +- Die Blocknummer ist Null – egal, wie lange es her ist, dass Sie den Testanbieter konfiguriert haben. Im Gegensatz zum echten Ethereum-Netzwerk, das alle 12 Sekunden einen neuen Block hinzufügt, wartet diese Simulation, bis Sie ihr Arbeit geben. +- `transactions` ist aus demselben Grund eine leere Liste: Wir haben noch nichts getan. Dieser erste Block ist ein **leerer Block**, nur um die Kette zu starten. +- Beachten Sie, dass der `parentHash` nur eine Reihe von leeren Bytes ist. Dies bedeutet, dass es sich um den ersten Block in der Kette handelt, auch bekannt als **Genesis-Block**. -## 3. Stop auf der Tour: [Transaktionen](/developers/docs/transactions/) {#tour-stop-3-transactions} +## Tour-Stopp Nr. 3: [Transaktionen](/developers/docs/transactions/) {#tour-stop-3-transactions} -Wir verharren bei Block Null bis es eine Transaktion zum Minen gibt, also geben wir ihm eine. Senden Sie ein paar Test-Ether von einem Konto zum anderem: +Wir stecken bei Block Null fest, bis eine Transaktion ansteht, also geben wir ihm eine. Senden Sie ein paar Test-Ether von einem Konto zum anderen: ```python In [10]: tx_hash = w3.eth.send_transaction({ 'from': w3.eth.accounts[0], 'to': w3.eth.accounts[1], - 'value': w3.toWei(3, 'ether'), + 'value': w3.to_wei(3, 'ether'), 'gas': 21000 }) ``` -Das ist typischerweise der Punkt, an dem Sie mehrere Sekunden warten würden, bis Ihre Transaktion in einem neuen Block erstellt wurde. Der gesamte Prozess läuft wie folgt ab: +Dies ist normalerweise der Punkt, an dem Sie mehrere Sekunden warten müssten, bis Ihre Transaktion in einen neuen Block aufgenommen wird. Der gesamte Prozess läuft ungefähr so ab: -1. Übermitteln Sie eine Transaktion und halten Sie den Transaktions-Hash bereit. Die Transaktion ist ausstehend bis sie geminted wurde. `tx_hash = w3.eth.send_transaction({ … })` -2. Warten Sie, bis die Transaktion geminted wurde: `w3.eth.wait_for_transaction_receipt(tx_hash)` -3. Setzen Sie die Anwendungslogik fort. Um die erfolgreiche Transaktion anzuzeigen: `w3.eth.get_transaction(tx_hash)` +1. Senden Sie eine Transaktion und behalten Sie den Transaktions-Hash. Bis der Block mit der Transaktion erstellt und übertragen wird, ist die Transaktion „ausstehend“. + `tx_hash = w3.eth.send_transaction({ … })` +2. Warten Sie, bis die Transaktion in einen Block aufgenommen wird: + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. Setzen Sie die Anwendungslogik fort. Um die erfolgreiche Transaktion anzuzeigen: + `w3.eth.get_transaction(tx_hash)` -Unsere simulierte Umgebung wird die Transaktion sofort in einem neuen Block hinzufügen, so dass wir die Transaktion direkt sehen können: +Unsere simulierte Umgebung fügt die Transaktion sofort in einem neuen Block hinzu, sodass wir die Transaktion sofort anzeigen können: ```python In [11]: w3.eth.get_transaction(tx_hash) @@ -274,22 +277,24 @@ Out[11]: AttributeDict({ }) ``` -Hier werden sie einige bekannte Details sehen: Die Felder `from`, `to` und `value` sollten mit den Einträgen unseres `send_transaction`-Aufrufs übereinstimmen. Das andere beruhigende Aspekt ist, dass diese Transaktion als erste Transaktion (`'transactionIndex': 0`) in Block Nr. 1 enthalten war. +Hier sehen Sie einige vertraute Details: Die Felder `from`, `to` und `value` sollten mit den Eingaben unseres `send_transaction`-Aufrufs übereinstimmen. Der andere beruhigende Aspekt ist, dass diese Transaktion als erste Transaktion (`'transactionIndex': 0`) in Block Nummer 1 aufgenommen wurde. -Wir können den Erfolg dieser Transaktion auch leicht überprüfen, indem wir die Salden der beiden beteiligten Konten kontrollieren. Drei Ether sollten sich von einem auf das andere Konto bewegt haben. +Wir können den Erfolg dieser Transaktion auch leicht überprüfen, indem wir die Salden der beiden beteiligten Konten kontrollieren. Drei Ether sollten von einem zum anderen transferiert worden sein. ```python In [12]: w3.eth.get_balance(w3.eth.accounts[0]) -Out[12]: 999996999999999999969000 +Out[12]: 999996999979000000000000 In [13]: w3.eth.get_balance(w3.eth.accounts[1]) Out[13]: 1000003000000000000000000 ``` -Letzteres sieht gut aus. Der Saldo hat sich von 1.000.000 auf 1.000.003 Ether erhöht. Aber was ist mit dem ersten Konto passiert? Es scheint etwas mehr, als drei Ether verloren zu haben. Leider ist nichts im Leben kostenlos. Die Nutzung des öffentlichen Netzes von Ethereum erfordert, dass das Netzwerk für seine Unterstützung eine Aufwandsentschädigung erhält. Eine kleine Transaktionsgebühr wurde vom Konto abgezogen, in Größenordnung von 31000 Wei. +Letzteres sieht gut aus! Der Saldo stieg von 1.000.000 auf 1.000.003 Ether. Aber was ist mit dem ersten Konto passiert? Es scheint etwas mehr als drei Ether verloren zu haben. Leider ist nichts im Leben umsonst, und die Nutzung des öffentlichen Ethereum-Netzwerks erfordert, dass Sie Ihre Peers für ihre unterstützende Rolle entschädigen. Eine kleine Transaktionsgebühr wurde von dem Konto abgezogen, das die Transaktion übermittelt hat – diese Gebühr ist die Menge des verbrauchten Gases (21.000 Gaseinheiten für eine ETH-Überweisung) multipliziert mit einer Grundgebühr, die je nach Netzwerkaktivität variiert, plus einem Trinkgeld, das an den Validator geht, der die Transaktion in einen Block aufnimmt. + +Mehr zu [Gas](/developers/docs/gas/#post-london) -Hinweis: Im öffentlichen Netzwerk basieren Transaktionsgebühren auf variablen Netzanforderungen und wie schnell Sie eine Transaktion verarbeiten möchten. Wenn Sie an einer Aufschlüsselung der Berechnung der Gebühren interessiert sind, sehen Sie sich meinen früheren Beitrag auf "Wie Transaktionen in einem Block enthalten sind" an. +Hinweis: Im öffentlichen Netzwerk sind die Transaktionsgebühren variabel und basieren auf der Netzwerknachfrage und der gewünschten Verarbeitungsgeschwindigkeit einer Transaktion. Wenn Sie an einer Aufschlüsselung der Gebührenberechnung interessiert sind, lesen Sie meinen früheren Beitrag darüber, wie Transaktionen in einen Block aufgenommen werden. -## Und atmen {#and-breathe} +## Und durchatmen {#and-breathe} -Wir sind schon eine ganze Weile dabei, daher ist jetzt ein guter Zeitpunkt für eine Pause. Im zweiten Teil unserer Serie befassen wir uns weiter mit der Materie. Einige der weiteren Konzepte: Verbindung zu einem echten Node, Smart Contracts und Token. Haben Sie weitere Fragen? Lassen Sie es mich wissen. Ihr Feedback hat Einfluss darauf, wohin die Reise geht. Anfragen können Sie gerne über [Twitter](https://twitter.com/wolovim) stellen. +Wir sind schon eine ganze Weile dabei, also scheint dies ein guter Zeitpunkt für eine Pause zu sein. Der Kaninchenbau geht weiter, und wir werden im zweiten Teil dieser Serie weiterforschen. Einige der kommenden Konzepte: Verbindung zu einem echten Node, Smart Contracts und Token. Haben Sie weitere Fragen? Lassen Sie es mich wissen! Ihr Feedback wird beeinflussen, wie wir von hier aus weitermachen. Anfragen sind über [Twitter](https://twitter.com/wolovim) willkommen. diff --git a/public/content/translations/de/developers/tutorials/all-you-can-cache/index.md b/public/content/translations/de/developers/tutorials/all-you-can-cache/index.md new file mode 100644 index 00000000000..d24759b06a8 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/all-you-can-cache/index.md @@ -0,0 +1,866 @@ +--- +title: "Alles, was Sie cachen können" +description: "Erfahren Sie, wie Sie einen Caching-Vertrag für günstigere Rollup-Transaktionen erstellen und verwenden." +author: Ori Pomerantz +tags: [ "Layer 2", "Caching", "Speicher" ] +skill: intermediate +published: 2022-09-15 +lang: de +--- + +Bei der Verwendung von Rollups sind die Kosten für ein Byte in der Transaktion viel höher als die Kosten für einen Storage-Slot. Daher ist es sinnvoll, so viele Informationen wie möglich on-chain zu cachen. + +In diesem Artikel erfahren Sie, wie Sie einen Caching-Vertrag so erstellen und verwenden, dass jeder wahrscheinlich mehrfach genutzte Parameterwert zwischengespeichert wird. Nach der ersten Verwendung ist er dann mit einer viel kleineren Anzahl von Bytes verfügbar. Außerdem lernen Sie, wie Sie Off-Chain-Code schreiben, der diesen Cache nutzt. + +Wenn Sie den Artikel überspringen und nur den Quellcode sehen möchten, [finden Sie ihn hier](https://github.com/qbzzt/20220915-all-you-can-cache). Der Entwicklungs-Stack ist [Foundry](https://getfoundry.sh/introduction/installation/). + +## Gesamtdesign {#overall-design} + +Der Einfachheit halber gehen wir davon aus, dass alle Transaktionsparameter `uint256` sind und eine Länge von 32 Bytes haben. Wenn wir eine Transaktion erhalten, parsen wir jeden Parameter wie folgt: + +1. Wenn das erste Byte `0xFF` ist, nehmen Sie die nächsten 32 Bytes als Parameterwert und schreiben Sie ihn in den Cache. + +2. Wenn das erste Byte `0xFE` ist, nehmen Sie die nächsten 32 Bytes als Parameterwert, aber schreiben Sie ihn _nicht_ in den Cache. + +3. Für jeden anderen Wert nehmen Sie die oberen vier Bits als Anzahl der zusätzlichen Bytes und die unteren vier Bits als die höchstwertigen Bits des Cache-Schlüssels. Hier sind einige Beispiele: + + | Bytes in Calldata | Cache-Schlüssel | + | :---------------- | --------------: | + | 0x0F | 0x0F | + | 0x10,0x10 | 0x10 | + | 0x12,0xAC | 0x02AC | + | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | + +## Cache-Manipulation {#cache-manipulation} + +Der Cache ist in [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol) implementiert. Gehen wir ihn Zeile für Zeile durch. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + + +contract Cache { + + bytes1 public constant INTO_CACHE = 0xFF; + bytes1 public constant DONT_CACHE = 0xFE; +``` + +Diese Konstanten werden verwendet, um die Sonderfälle zu interpretieren, in denen wir alle Informationen bereitstellen und sie entweder in den Cache geschrieben haben wollen oder nicht. Das Schreiben in den Cache erfordert zwei [`SSTORE`](https://www.evm.codes/#55)-Operationen in zuvor ungenutzte Storage-Slots zu einem Preis von je 22100 Gas, daher machen wir es optional. + +```solidity + + mapping(uint => uint) public val2key; +``` + +Ein [Mapping](https://www.geeksforgeeks.org/solidity/solidity-mappings/) zwischen den Werten und ihren Schlüsseln. Diese Information ist notwendig, um Werte zu kodieren, bevor Sie die Transaktion versenden. + +```solidity + // An Position n befindet sich der Wert für den Schlüssel n+1, weil wir die Null + // als „nicht im Cache“ reservieren müssen. + uint[] public key2val; +``` + +Wir können ein Array für die Zuordnung von Schlüsseln zu Werten verwenden, da wir die Schlüssel zuweisen und dies der Einfachheit halber sequenziell tun. + +```solidity + function cacheRead(uint _key) public view returns (uint) { + require(_key <= key2val.length, "Lese nicht initialisierten Cache-Eintrag"); + return key2val[_key-1]; + } // cacheRead +``` + +Lesen Sie einen Wert aus dem Cache. + +```solidity + // Einen Wert in den Cache schreiben, falls er noch nicht vorhanden ist + // Nur öffentlich, damit der Test funktioniert + function cacheWrite(uint _value) public returns (uint) { + // Wenn der Wert bereits im Cache ist, geben Sie den aktuellen Schlüssel zurück + if (val2key[_value] != 0) { + return val2key[_value]; + } +``` + +Es hat keinen Sinn, denselben Wert mehr als einmal in den Cache zu legen. Wenn der Wert bereits vorhanden ist, geben Sie einfach den vorhandenen Schlüssel zurück. + +```solidity + // Da 0xFE ein Sonderfall ist, ist der größte Schlüssel, den der Cache + // halten kann, 0x0D gefolgt von 15 0xFFs. Wenn die Cache-Länge bereits so + // groß ist, schlägt der Vorgang fehl. + // 1 2 3 4 5 6 7 8 9 A B C D E F + require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + "Cache-Überlauf"); +``` + +Ich glaube nicht, dass wir jemals einen so großen Cache erhalten werden (ca. 1,8\*1037 Einträge, für deren Speicherung etwa 1027 TB erforderlich wären). Ich bin jedoch alt genug, um mich an [„640 KB wären immer genug“](https://quoteinvestigator.com/2011/09/08/640k-enough/) zu erinnern. Dieser Test ist sehr günstig. + +```solidity + // Den Wert mit dem nächsten Schlüssel schreiben + val2key[_value] = key2val.length+1; +``` + +Fügen Sie die umgekehrte Suche hinzu (vom Wert zum Schlüssel). + +```solidity + key2val.push(_value); +``` + +Fügen Sie die Vorwärtssuche hinzu (vom Schlüssel zum Wert). Da wir die Werte sequenziell zuweisen, können wir sie einfach nach dem letzten Array-Wert hinzufügen. + +```solidity + return key2val.length; + } // cacheWrite +``` + +Gibt die neue Länge von `key2val` zurück, d. h. die Zelle, in der der neue Wert gespeichert ist. + +```solidity + function _calldataVal(uint startByte, uint length) + private pure returns (uint) +``` + +Diese Funktion liest einen Wert aus Calldata beliebiger Länge (bis zu 32 Bytes, der Wortgröße). + +```solidity + { + uint _retVal; + + require(length < 0x21, + "_calldataVal-Längenlimit beträgt 32 Bytes"); + require(length + startByte <= msg.data.length, + "_calldataVal versucht, über die Calldata-Größe hinaus zu lesen"); +``` + +Diese Funktion ist intern. Wenn also der Rest des Codes korrekt geschrieben ist, sind diese Tests nicht erforderlich. Sie kosten jedoch nicht viel, also können wir sie auch gleich einbauen. + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +Dieser Code ist in [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html). Er liest einen 32-Byte-Wert aus Calldata. Dies funktioniert auch, wenn Calldata vor `startByte+32` aufhört, da nicht initialisierter Speicherplatz in der EVM als Null betrachtet wird. + +```solidity + _retVal = _retVal >> (256-length*8); +``` + +Wir wollen nicht unbedingt einen 32-Byte-Wert. Dadurch werden die überzähligen Bytes entfernt. + +```solidity + return _retVal; + } // _calldataVal + + + // Einen einzelnen Parameter aus Calldata lesen, beginnend bei _fromByte + function _readParam(uint _fromByte) internal + returns (uint _nextByte, uint _parameterValue) + { +``` + +Lesen Sie einen einzelnen Parameter aus Calldata. Beachten Sie, dass wir nicht nur den gelesenen Wert zurückgeben müssen, sondern auch den Ort des nächsten Bytes, da die Parameter von 1 Byte bis zu 33 Bytes lang sein können. + +```solidity + // Das erste Byte sagt uns, wie der Rest zu interpretieren ist + uint8 _firstByte; + + _firstByte = uint8(_calldataVal(_fromByte, 1)); +``` + +Solidity versucht, die Anzahl der Fehler zu reduzieren, indem es potenziell gefährliche [implizite Typumwandlungen](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions) verbietet. Ein Downgrade, zum Beispiel von 256 Bit auf 8 Bit, muss explizit sein. + +```solidity + + // Den Wert lesen, aber nicht in den Cache schreiben + if (_firstByte == uint8(DONT_CACHE)) + return(_fromByte+33, _calldataVal(_fromByte+1, 32)); + + // Den Wert lesen und in den Cache schreiben + if (_firstByte == uint8(INTO_CACHE)) { + uint _param = _calldataVal(_fromByte+1, 32); + cacheWrite(_param); + return(_fromByte+33, _param); + } + + // Wenn wir hier angekommen sind, bedeutet das, dass wir aus dem Cache lesen müssen + + // Anzahl der zusätzlich zu lesenden Bytes + uint8 _extraBytes = _firstByte / 16; +``` + +Nehmen Sie das untere [Nibble](https://en.wikipedia.org/wiki/Nibble) und kombinieren Sie es mit den anderen Bytes, um den Wert aus dem Cache zu lesen. + +```solidity + uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) + + _calldataVal(_fromByte+1, _extraBytes); + + return (_fromByte+_extraBytes+1, cacheRead(_key)); + + } // _readParam + + + // n Parameter lesen (Funktionen wissen, wie viele Parameter sie erwarten) + function _readParams(uint _paramNum) internal returns (uint[] memory) { +``` + +Wir könnten die Anzahl der Parameter, die wir haben, aus Calldata selbst entnehmen, aber die Funktionen, die uns aufrufen, wissen, wie viele Parameter sie erwarten. Es ist einfacher, wenn sie es uns sagen. + +```solidity + // Die Parameter, die wir lesen + uint[] memory params = new uint[](_paramNum); + + // Die Parameter beginnen bei Byte 4, davor steht die Funktionssignatur + uint _atByte = 4; + + for(uint i=0; i<_paramNum; i++) { + (_atByte, params[i]) = _readParam(_atByte); + } +``` + +Lesen Sie die Parameter, bis Sie die benötigte Anzahl haben. Wenn wir das Ende von Calldata überschreiten, wird `_readParams` den Aufruf rückgängig machen. + +```solidity + + return(params); + } // readParams + + // Zum Testen von _readParams, das Lesen von vier Parametern testen + function fourParam() public + returns (uint256,uint256,uint256,uint256) + { + uint[] memory params; + params = _readParams(4); + return (params[0], params[1], params[2], params[3]); + } // fourParam +``` + +Ein großer Vorteil von Foundry ist, dass Tests in Solidity geschrieben werden können ([siehe Testen des Caches unten](#testing-the-cache)). Dies macht Unit-Tests viel einfacher. Dies ist eine Funktion, die vier Parameter liest und sie zurückgibt, damit der Test überprüfen kann, ob sie korrekt waren. + +```solidity + // Einen Wert abrufen, Bytes zurückgeben, die ihn kodieren (wenn möglich unter Verwendung des Caches) + function encodeVal(uint _val) public view returns(bytes memory) { +``` + +`encodeVal` ist eine Funktion, die Off-Chain-Code aufruft, um Calldata zu erstellen, die den Cache nutzen. Sie empfängt einen einzelnen Wert und gibt die Bytes zurück, die ihn kodieren. Diese Funktion ist eine `view`, erfordert also keine Transaktion und kostet bei externem Aufruf kein Gas. + +```solidity + uint _key = val2key[_val]; + + // Der Wert ist noch nicht im Cache, fügen Sie ihn hinzu + if (_key == 0) + return bytes.concat(INTO_CACHE, bytes32(_val)); +``` + +In der [EVM](/developers/docs/evm/) wird davon ausgegangen, dass jeder nicht initialisierte Speicher Null ist. Wenn wir also nach dem Schlüssel für einen nicht vorhandenen Wert suchen, erhalten wir eine Null. In diesem Fall sind die Bytes, die ihn kodieren, `INTO_CACHE` (damit er beim nächsten Mal zwischengespeichert wird), gefolgt von dem tatsächlichen Wert. + +```solidity + // Wenn der Schlüssel <0x10 ist, geben Sie ihn als einzelnes Byte zurück + if (_key < 0x10) + return bytes.concat(bytes1(uint8(_key))); +``` + +Einzelne Bytes sind am einfachsten. Wir verwenden einfach [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat), um einen `bytes`-Typ in ein Byte-Array zu verwandeln, das eine beliebige Länge haben kann. Trotz des Namens funktioniert es auch mit nur einem Argument einwandfrei. + +```solidity + // Zwei-Byte-Wert, kodiert als 0x1vvv + if (_key < 0x1000) + return bytes.concat(bytes2(uint16(_key) | 0x1000)); +``` + +Wenn wir einen Schlüssel haben, der kleiner als 163 ist, können wir ihn in zwei Bytes ausdrücken. Wir konvertieren zuerst `_key`, einen 256-Bit-Wert, in einen 16-Bit-Wert und verwenden ein logisches Oder, um die Anzahl der zusätzlichen Bytes zum ersten Byte hinzuzufügen. Dann wandeln wir es einfach in einen `bytes2`-Wert um, der in `bytes` umgewandelt werden kann. + +```solidity + // Es gibt wahrscheinlich eine clevere Möglichkeit, die folgenden Zeilen als Schleife auszuführen, + // aber es ist eine View-Funktion, also optimiere ich für die Zeit des Programmierers und + // die Einfachheit. + + if (_key < 16*256**2) + return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2))); + if (_key < 16*256**3) + return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3))); + . + . + . + if (_key < 16*256**14) + return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14))); + if (_key < 16*256**15) + return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15))); +``` + +Die anderen Werte (3 Bytes, 4 Bytes usw.) werden auf die gleiche Weise behandelt, nur mit unterschiedlichen Feldgrößen. + +```solidity + // Wenn wir hier ankommen, ist etwas falsch. + revert("Fehler in encodeVal, sollte nicht passieren"); +``` + +Wenn wir hier ankommen, bedeutet das, dass wir einen Schlüssel erhalten haben, der nicht kleiner als 16\*25615 ist. Aber `cacheWrite` begrenzt die Schlüssel, sodass wir nicht einmal bis zu 14\*25616 kommen (was ein erstes Byte von 0xFE hätte, also wie `DONT_CACHE` aussehen würde). Aber es kostet uns nicht viel, einen Test hinzuzufügen, für den Fall, dass ein zukünftiger Programmierer einen Fehler einbaut. + +```solidity + } // encodeVal + +} // Cache +``` + +### Testen des Caches {#testing-the-cache} + +Einer der Vorteile von Foundry ist, dass [Sie Tests in Solidity schreiben können](https://getfoundry.sh/forge/tests/overview/), was das Schreiben von Unit-Tests erleichtert. Die Tests für die `Cache`-Klasse sind [hier](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol). Da der Testcode repetitiv ist, wie es bei Tests oft der Fall ist, werden in diesem Artikel nur die interessanten Teile erklärt. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + + +// Need to run `forge test -vv` for the console. +import "forge-std/console.sol"; +``` + +Dies ist nur eine Boilerplate, die notwendig ist, um das Testpaket und `console.log` zu verwenden. + +```solidity +import "src/Cache.sol"; +``` + +Wir müssen den Vertrag kennen, den wir testen. + +```solidity +contract CacheTest is Test { + Cache cache; + + function setUp() public { + cache = new Cache(); + } +``` + +Die `setUp`-Funktion wird vor jedem Test aufgerufen. In diesem Fall erstellen wir einfach einen neuen Cache, damit sich unsere Tests nicht gegenseitig beeinflussen. + +```solidity + function testCaching() public { +``` + +Tests sind Funktionen, deren Namen mit `test` beginnen. Diese Funktion überprüft die grundlegende Cache-Funktionalität, das Schreiben von Werten und das erneute Lesen. + +```solidity + for(uint i=1; i<5000; i++) { + cache.cacheWrite(i*i); + } + + for(uint i=1; i<5000; i++) { + assertEq(cache.cacheRead(i), i*i); +``` + +So führen Sie das eigentliche Testen mit [`assert...`-Funktionen](https://getfoundry.sh/reference/forge-std/std-assertions/) durch. In diesem Fall prüfen wir, ob der Wert, den wir geschrieben haben, auch der ist, den wir lesen. Wir können das Ergebnis von `cache.cacheWrite` verwerfen, da wir wissen, dass Cache-Schlüssel linear vergeben werden. + +```solidity + } + } // testCaching + + + // Cache the same value multiple times, ensure that the key stays + // the same + function testRepeatCaching() public { + for(uint i=1; i<100; i++) { + uint _key1 = cache.cacheWrite(i); + uint _key2 = cache.cacheWrite(i); + assertEq(_key1, _key2); + } +``` + +Zuerst schreiben wir jeden Wert zweimal in den Cache und stellen sicher, dass die Schlüssel identisch sind (was bedeutet, dass der zweite Schreibvorgang nicht wirklich stattgefunden hat). + +```solidity + for(uint i=1; i<100; i+=3) { + uint _key = cache.cacheWrite(i); + assertEq(_key, i); + } + } // testRepeatCaching +``` + +Theoretisch könnte es einen Fehler geben, der sich nicht auf aufeinanderfolgende Cache-Schreibvorgänge auswirkt. Hier führen wir also einige Schreibvorgänge durch, die nicht aufeinanderfolgen, und sehen, dass die Werte immer noch nicht neu geschrieben werden. + +```solidity + // Read a uint from a memory buffer (to make sure we get back the parameters + // we sent out) + function toUint256(bytes memory _bytes, uint256 _start) internal pure + returns (uint256) +``` + +Liest ein 256-Bit-Wort aus einem `bytes memory`-Puffer. Mit dieser Hilfsfunktion können wir überprüfen, ob wir die richtigen Ergebnisse erhalten, wenn wir einen Funktionsaufruf ausführen, der den Cache verwendet. + +```solidity + { + require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); + uint256 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x20), _start)) + } +``` + +Yul unterstützt keine Datenstrukturen jenseits von `uint256`. Wenn Sie also auf eine komplexere Datenstruktur wie den Speicherpuffer `_bytes` verweisen, erhalten Sie die Adresse dieser Struktur. Solidity speichert `bytes memory`-Werte als 32-Byte-Wort, das die Länge enthält, gefolgt von den eigentlichen Bytes. Um also Byte-Nummer `_start` zu erhalten, müssen wir `_bytes+32+_start` berechnen. + +```solidity + + return tempUint; + } // toUint256 + + // Function signature for fourParams(), courtesy of + // 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 + uint256 constant VAL_A = 0xDEAD60A7; + uint256 constant VAL_B = 0xBEEF; + uint256 constant VAL_C = 0x600D; + uint256 constant VAL_D = 0x600D60A7; +``` + +Einige Konstanten, die wir für Tests benötigen. + +```solidity + function testReadParam() public { +``` + +Rufen Sie `fourParams()` auf, eine Funktion, die `readParams` verwendet, um zu testen, ob wir Parameter korrekt lesen können. + +```solidity + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; +``` + +Wir können den normalen ABI-Mechanismus nicht verwenden, um eine Funktion über den Cache aufzurufen, daher müssen wir den Low-Level-Mechanismus [`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses) verwenden. Dieser Mechanismus verwendet `bytes memory` als Eingabe und gibt diese (sowie einen booleschen Wert) als Ausgabe zurück. + +```solidity + // Erster Aufruf, der Cache ist leer + _callInput = bytes.concat( + FOUR_PARAMS, +``` + +Es ist nützlich, wenn derselbe Vertrag sowohl zwischengespeicherte Funktionen (für Aufrufe direkt aus Transaktionen) als auch nicht zwischengespeicherte Funktionen (für Aufrufe von anderen Smart Contracts) unterstützt. Dazu müssen wir uns weiterhin auf den Solidity-Mechanismus verlassen, um die richtige Funktion aufzurufen, anstatt alles in [eine `fallback`-Funktion](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function) zu packen. Dies macht die Zusammensetzbarkeit wesentlich einfacher. Ein einzelnes Byte würde in den meisten Fällen ausreichen, um die Funktion zu identifizieren, sodass wir drei Bytes (16\*3=48 Gas) verschwenden. Als ich das schrieb, kosteten diese 48 Gas jedoch 0,07 Cent, was ein angemessener Preis für einfacheren, weniger fehleranfälligen Code ist. + +```solidity + // Erster Wert, zum Cache hinzufügen + cache.INTO_CACHE(), + bytes32(VAL_A), +``` + +Der erste Wert: Eine Markierung, die besagt, dass es sich um einen vollständigen Wert handelt, der in den Cache geschrieben werden muss, gefolgt von den 32 Bytes des Wertes. Die anderen drei Werte sind ähnlich, mit der Ausnahme, dass `VAL_B` nicht in den Cache geschrieben wird und `VAL_C` sowohl der dritte als auch der vierte Parameter ist. + +```solidity + . + . + . + ); + (_success, _callOutput) = _cacheAddr.call(_callInput); +``` + +Hier rufen wir den `Cache`-Vertrag auf. + +```solidity + assertEq(_success, true); +``` + +Wir erwarten, dass der Anruf erfolgreich ist. + +```solidity + assertEq(cache.cacheRead(1), VAL_A); + assertEq(cache.cacheRead(2), VAL_C); +``` + +Wir beginnen mit einem leeren Cache und fügen dann `VAL_A` gefolgt von `VAL_C` hinzu. Wir erwarten, dass der erste den Schlüssel 1 und der zweite den Schlüssel 2 hat. + +``` + assertEq(toUint256(_callOutput,0), VAL_A); + assertEq(toUint256(_callOutput,32), VAL_B); + assertEq(toUint256(_callOutput,64), VAL_C); + assertEq(toUint256(_callOutput,96), VAL_C); +``` + +Die Ausgabe sind die vier Parameter. Hier überprüfen wir, ob es richtig ist. + +```solidity + // Zweiter Aufruf, wir können den Cache verwenden + _callInput = bytes.concat( + FOUR_PARAMS, + + // Erster Wert im Cache + bytes1(0x01), +``` + +Cache-Schlüssel unter 16 sind nur ein Byte lang. + +```solidity + // Zweiter Wert, nicht zum Cache hinzufügen + cache.DONT_CACHE(), + bytes32(VAL_B), + + // Dritter und vierter Wert, gleicher Wert + bytes1(0x02), + bytes1(0x02) + ); + . + . + . + } // testReadParam +``` + +Die Tests nach dem Aufruf sind identisch mit denen nach dem ersten Aufruf. + +```solidity + function testEncodeVal() public { +``` + +Diese Funktion ist ähnlich wie `testReadParam`, nur dass wir die Parameter nicht explizit schreiben, sondern `encodeVal()` verwenden. + +```solidity + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(VAL_A), + cache.encodeVal(VAL_B), + cache.encodeVal(VAL_C), + cache.encodeVal(VAL_D) + ); + . + . + . + assertEq(_callInput.length, 4+1*4); + } // testEncodeVal +``` + +Der einzige zusätzliche Test in `testEncodeVal()` besteht darin, zu überprüfen, ob die Länge von `_callInput` korrekt ist. Für den ersten Aufruf ist es 4+33\*4. Beim zweiten, bei dem jeder Wert bereits im Cache ist, ist es 4+1\*4. + +```solidity + // Testen Sie encodeVal, wenn der Schlüssel mehr als ein einzelnes Byte hat + // Maximal drei Bytes, da das Füllen des Caches auf vier Bytes zu lange dauert. + function testEncodeValBig() public { + // Legen Sie eine Reihe von Werten in den Cache. + // Um die Dinge einfach zu halten, verwenden Sie den Schlüssel n für den Wert n. + for(uint i=1; i<0x1FFF; i++) { + cache.cacheWrite(i); + } +``` + +Die obige Funktion `testEncodeVal` schreibt nur vier Werte in den Cache, so dass [der Teil der Funktion, der sich mit Multi-Byte-Werten befasst](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171), nicht überprüft wird. Aber dieser Code ist kompliziert und fehleranfällig. + +Der erste Teil dieser Funktion ist eine Schleife, die alle Werte von 1 bis 0x1FFF in der richtigen Reihenfolge in den Cache schreibt, damit wir diese Werte kodieren können und wissen, wohin sie gehen. + +```solidity + . + . + . + + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(0x000F), // Ein Byte 0x0F + cache.encodeVal(0x0010), // Zwei Bytes 0x1010 + cache.encodeVal(0x0100), // Zwei Bytes 0x1100 + cache.encodeVal(0x1000) // Drei Bytes 0x201000 + ); +``` + +Testen Sie Ein-Byte-, Zwei-Byte- und Drei-Byte-Werte. Wir testen nicht darüber hinaus, da es zu lange dauern würde, genügend Stack-Einträge zu schreiben (mindestens 0x10000000, etwa eine Viertelmilliarde). + +```solidity + . + . + . + . + } // testEncodeValBig + + + // Test, was bei einem zu kleinen Puffer zu einem Revert führt + function testShortCalldata() public { +``` + +Testen Sie, was im anormalen Fall passiert, wenn nicht genügend Parameter vorhanden sind. + +```solidity + . + . + . + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, false); + } // testShortCalldata +``` + +Da es zurückgesetzt wird, sollte das Ergebnis, das wir erhalten, `false` sein. + +``` + // Aufruf mit nicht vorhandenen Cache-Schlüsseln + function testNoCacheKey() public { + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + + // Erster Wert, zum Cache hinzufügen + cache.INTO_CACHE(), + bytes32(VAL_A), + + // Zweiter Wert + bytes1(0x0F), + bytes2(0x1234), + bytes11(0xA10102030405060708090A) + ); +``` + +Diese Funktion erhält vier vollkommen legitime Parameter, nur dass der Cache leer ist und es keine Werte zum Lesen gibt. + +```solidity + . + . + . + // Test, ob bei einem übermäßig langen Puffer alles funktioniert + function testLongCalldata() public { + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; + + // Erster Aufruf, der Cache ist leer + _callInput = bytes.concat( + FOUR_PARAMS, + + // Erster Wert, zum Cache hinzufügen + cache.INTO_CACHE(), bytes32(VAL_A), + + // Zweiter Wert, zum Cache hinzufügen + cache.INTO_CACHE(), bytes32(VAL_B), + + // Dritter Wert, zum Cache hinzufügen + cache.INTO_CACHE(), bytes32(VAL_C), + + // Vierter Wert, zum Cache hinzufügen + cache.INTO_CACHE(), bytes32(VAL_D), + + // Und ein weiterer Wert für „Viel Glück“ + bytes4(0x31112233) + ); +``` + +Diese Funktion sendet fünf Werte. Wir wissen, dass der fünfte Wert ignoriert wird, weil er kein gültiger Cache-Eintrag ist, was zu einer Zurückweisung geführt hätte, wenn er nicht enthalten gewesen wäre. + +```solidity + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, true); + . + . + . + } // testLongCalldata + +} // CacheTest + +``` + +## Eine Beispielanwendung {#a-sample-app} + +Tests in Solidity zu schreiben ist schön und gut, aber am Ende muss eine Dapp in der Lage sein, Anfragen von außerhalb der Kette zu verarbeiten, um nützlich zu sein. Dieser Artikel zeigt, wie man Caching in einer Dapp mit `WORM` verwendet, was für „Write Once, Read Many“ steht. Wenn ein Schlüssel noch nicht geschrieben ist, können Sie einen Wert darauf schreiben. Wenn der Schlüssel bereits geschrieben ist, erhalten Sie einen Revert. + +### Der Vertrag {#the-contract} + +[Dies ist der Vertrag](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol). Es wiederholt größtenteils, was wir bereits mit `Cache` und `CacheTest` gemacht haben, also behandeln wir nur die Teile, die interessant sind. + +```solidity +import "./Cache.sol"; + +contract WORM is Cache { +``` + +Der einfachste Weg, `Cache` zu verwenden, ist, es in unserem eigenen Vertrag zu erben. + +```solidity + function writeEntryCached() external { + uint[] memory params = _readParams(2); + writeEntry(params[0], params[1]); + } // writeEntryCached +``` + +Diese Funktion ist ähnlich wie `fourParam` in `CacheTest` oben. Da wir die ABI-Spezifikationen nicht befolgen, ist es am besten, keine Parameter in der Funktion zu deklarieren. + +```solidity + // Machen Sie es einfacher, uns anzurufen + // Funktionssignatur für writeEntryCached(), mit freundlicher Genehmigung von + // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3 + bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3; +``` + +Der externe Code, der `writeEntryCached` aufruft, muss die Calldata manuell erstellen, anstatt `worm.writeEntryCached` zu verwenden, da wir die ABI-Spezifikationen nicht befolgen. Dieser konstante Wert erleichtert das Schreiben. + +Beachten Sie, dass wir `WRITE_ENTRY_CACHED` zwar als Zustandsvariable definieren, zum externen Lesen jedoch die Getter-Funktion `worm.WRITE_ENTRY_CACHED()` verwendet werden muss. + +```solidity + function readEntry(uint key) public view + returns (uint _value, address _writtenBy, uint _writtenAtBlock) +``` + +Die Lesefunktion ist eine `View`-Funktion, erfordert also keine Transaktion und kostet kein Gas. Daher gibt es keinen Vorteil, den Cache für den Parameter zu verwenden. Bei Ansichtsfunktionen ist es am besten, den einfacheren Standardmechanismus zu verwenden. + +### Der Test-Code {#the-testing-code} + +[Dies ist der Test-Code für den Vertrag](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol). Nochmals, lassen Sie uns nur das Interessante betrachten. + +```solidity + function testWReadWrite() public { + worm.writeEntry(0xDEAD, 0x60A7); + + vm.expectRevert(bytes("Eintrag bereits geschrieben")); + worm.writeEntry(0xDEAD, 0xBEEF); +``` + +[Dies (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) ist, wie wir in einem Foundry-Test angeben, dass der nächste Aufruf fehlschlagen sollte, und der gemeldete Grund für einen Fehler. Dies gilt, wenn wir die Syntax `. verwenden()` anstatt die Calldata zu erstellen und den Vertrag über die Low-Level-Schnittstelle aufzurufen (`.call()` usw.). + +```solidity + function testReadWriteCached() public { + uint cacheGoat = worm.cacheWrite(0x60A7); +``` + +Hier verwenden wir die Tatsache, dass `cacheWrite` den Cache-Schlüssel zurückgibt. Dies ist nichts, was wir in der Produktion erwarten würden, da `cacheWrite` den Zustand ändert und daher nur während einer Transaktion aufgerufen werden kann. Transaktionen haben keine Rückgabewerte. Wenn sie Ergebnisse haben, sollen diese als Ereignisse ausgegeben werden. Der Rückgabewert von `cacheWrite` ist also nur von On-Chain-Code aus zugänglich, und On-Chain-Code benötigt kein Parameter-Caching. + +```solidity + (_success,) = address(worm).call(_callInput); +``` + +So teilen wir Solidity mit, dass `.call()` zwar zwei Rückgabewerte hat, wir aber nur am ersten interessiert sind. + +```solidity + (_success,) = address(worm).call(_callInput); + assertEq(_success, false); +``` + +Da wir die Low-Level-Funktion `
.call()` verwenden, können wir `vm.expectRevert()` nicht verwenden und müssen uns den booleschen Erfolgswert ansehen, den wir vom Aufruf erhalten. + +```solidity + event EntryWritten(uint indexed key, uint indexed value); + + . + . + . + + _callInput = bytes.concat( + worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b)); + vm.expectEmit(true, true, false, false); + emit EntryWritten(a, b); + (_success,) = address(worm).call(_callInput); +``` + +So überprüfen wir, ob der Code in Foundry [ein Ereignis korrekt ausgibt](https://getfoundry.sh/reference/cheatcodes/expect-emit/). + +### Der Client {#the-client} + +Was man mit Solidity-Tests nicht bekommt, ist JavaScript-Code, den man kopieren und in die eigene Anwendung einfügen kann. Um diesen Code zu schreiben, habe ich WORM im [Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli), dem neuen Testnet von [Optimism](https://www.optimism.io/), bereitgestellt. Es befindet sich an der Adresse [`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a). + +[Sie können den JavaScript-Code für den Client hier sehen](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js). So verwenden Sie es: + +1. Klonen Sie das Git-Repository: + + ```sh + git clone https://github.com/qbzzt/20220915-all-you-can-cache.git + ``` + +2. Installieren Sie die erforderlichen Pakete: + + ```sh + cd javascript + yarn + ``` + +3. Kopieren Sie die Konfigurationsdatei: + + ```sh + cp .env.example .env + ``` + +4. Bearbeiten Sie `.env` für Ihre Konfiguration: + + | Parameter | Wert | + | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | MNEMONIC | Die Mnemonik für ein Konto, das genug ETH hat, um eine Transaktion zu bezahlen. [Hier können Sie kostenlose ETH für das Optimism Goerli-Netzwerk erhalten](https://optimismfaucet.xyz/). | + | OPTIMISM_GOERLI_URL | URL zu Optimism Goerli. Der öffentliche Endpunkt `https://goerli.optimism.io` ist ratenbegrenzt, aber für das, was wir hier brauchen, ausreichend. | + +5. Führen Sie `index.js` aus. + + ```sh + node index.js + ``` + + Diese Beispielanwendung schreibt zunächst einen Eintrag in WORM und zeigt die Calldata und einen Link zur Transaktion auf Etherscan an. Dann liest es diesen Eintrag zurück und zeigt den verwendeten Schlüssel und die Werte im Eintrag an (Wert, Blocknummer und Autor). + +Der größte Teil des Clients ist normales Dapp-JavaScript. Auch hier gehen wir nur die interessanten Teile durch. + +```javascript +. +. +. +const main = async () => { + const func = await worm.WRITE_ENTRY_CACHED() + + // Jedes Mal einen neuen Schlüssel benötigen + const key = await worm.encodeVal(Number(new Date())) +``` + +Ein bestimmter Slot kann nur einmal beschrieben werden, daher verwenden wir den Zeitstempel, um sicherzustellen, dass wir keine Slots wiederverwenden. + +```javascript +const val = await worm.encodeVal("0x600D") + +// Einen Eintrag schreiben +const calldata = func + key.slice(2) + val.slice(2) +``` + +Ethers erwartet, dass die Aufrufdaten (call data) eine Hex-Zeichenfolge sind, also `0x` gefolgt von einer geraden Anzahl hexadezimaler Ziffern. Da `key` und `val` beide mit `0x` beginnen, müssen wir diese Header entfernen. + +```javascript +const tx = await worm.populateTransaction.writeEntryCached() +tx.data = calldata + +sentTx = await wallet.sendTransaction(tx) +``` + +Wie beim Solidity-Testcode können wir eine zwischengespeicherte Funktion nicht normal aufrufen. Stattdessen müssen wir einen Mechanismus auf niedrigerer Ebene verwenden. + +```javascript + . + . + . + // Lesen des gerade geschriebenen Eintrags + const realKey = '0x' + key.slice(4) // das FF-Flag entfernen + const entryRead = await worm.readEntry(realKey) + . + . + . +``` + +Zum Lesen von Einträgen können wir den normalen Mechanismus verwenden. Es ist nicht erforderlich, Parameter-Caching mit `view`-Funktionen zu verwenden. + +## Fazit {#conclusion} + +Der Code in diesem Artikel ist ein Proof of Concept, dessen Zweck es ist, die Idee leicht verständlich zu machen. Für ein produktionsreifes System sollten Sie möglicherweise einige zusätzliche Funktionen implementieren: + +- Behandeln Sie Werte, die nicht `uint256` sind. Zum Beispiel Zeichenketten. +- Anstelle eines globalen Caches könnten Sie eine Zuordnung zwischen Benutzern und Caches haben. Verschiedene Benutzer verwenden unterschiedliche Werte. +- Werte, die für Adressen verwendet werden, unterscheiden sich von denen, die für andere Zwecke verwendet werden. Es könnte sinnvoll sein, einen separaten Cache nur für Adressen zu haben. +- Derzeit basieren die Cache-Schlüssel auf einem „First come, smallest key“-Algorithmus. Die ersten sechzehn Werte können als einzelnes Byte gesendet werden. Die nächsten 4080 Werte können als zwei Bytes gesendet werden. Die nächste Million Werte sind drei Bytes usw. Ein Produktionssystem sollte Nutzungszähler für Cache-Einträge führen und diese so neu organisieren, dass die sechzehn _häufigsten_ Werte ein Byte, die nächsten 4080 häufigsten Werte zwei Bytes usw. sind. + + Dies ist jedoch eine potenziell gefährliche Operation. Stellen Sie sich die folgende Abfolge von Ereignissen vor: + + 1. Noam Naive ruft `encodeVal` auf, um die Adresse zu kodieren, an die er Token senden möchte. Diese Adresse ist eine der ersten, die in der Anwendung verwendet wird, daher ist der kodierte Wert 0x06. Dies ist eine `view`-Funktion, keine Transaktion, also ist es zwischen Noam und dem von ihm verwendeten Knoten, und niemand sonst weiß davon. + + 2. Owen Owner führt die Neuanordnung des Caches durch. Sehr wenige Leute verwenden diese Adresse tatsächlich, daher ist sie jetzt als 0x201122 kodiert. Ein anderer Wert, 1018, wird 0x06 zugewiesen. + + 3. Noam Naive sendet seine Token an 0x06. Sie gehen an die Adresse `0x0000000000000000000000000de0b6b3a7640000`, und da niemand den privaten Schlüssel für diese Adresse kennt, bleiben sie dort einfach stecken. Noam ist _nicht glücklich_. + + Es gibt Möglichkeiten, dieses Problem und das damit zusammenhängende Problem von Transaktionen, die sich während der Neuanordnung des Caches im Mempool befinden, zu lösen, aber Sie müssen sich dessen bewusst sein. + +Ich habe hier Caching mit Optimism demonstriert, weil ich ein Optimism-Mitarbeiter bin und dies das Rollup ist, das ich am besten kenne. Aber es sollte mit jedem Rollup funktionieren, das minimale Kosten für die interne Verarbeitung verlangt, sodass im Vergleich dazu das Schreiben der Transaktionsdaten auf L1 der Hauptaufwand ist. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). + diff --git a/public/content/translations/de/developers/tutorials/app-plasma/index.md b/public/content/translations/de/developers/tutorials/app-plasma/index.md new file mode 100644 index 00000000000..e3f86fa1768 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/app-plasma/index.md @@ -0,0 +1,1261 @@ +--- +title: "Schreiben Sie ein App-spezifisches Plasma, das die Privatsphäre wahrt" +description: "In diesem Tutorial bauen wir eine halbgeheime Bank für Einlagen. Die Bank ist eine zentralisierte Komponente; sie kennt das Guthaben jedes Benutzers. Diese Informationen werden jedoch nicht onchain gespeichert. Stattdessen veröffentlicht die Bank einen Hash des Zustands. Jedes Mal, wenn eine Transaktion stattfindet, veröffentlicht die Bank den neuen Hash, zusammen mit einem Zero-Knowledge-Proof, dass sie eine signierte Transaktion hat, die den Hash-Zustand in den neuen ändert. Nach der Lektüre dieses Tutorials werden Sie nicht nur verstehen, wie man Zero-Knowledge-Proofs verwendet, sondern auch, warum man sie verwendet und wie man dies sicher tut." +author: Ori Pomerantz +tags: + [ + "Zero-Knowledge", + "Server", + "Off-Chain", + "Privatsphäre" + ] +skill: advanced +lang: de +published: 2025-10-15 +--- + +## Einführung {#introduction} + +Im Gegensatz zu [Rollups](/developers/docs/scaling/zk-rollups/) nutzen [Plasmas](/developers/docs/scaling/plasma) das Ethereum-Mainnet für die Integrität, aber nicht für die Verfügbarkeit. In diesem Artikel schreiben wir eine Anwendung, die sich wie ein Plasma verhält, wobei Ethereum die Integrität (keine unbefugten Änderungen), aber nicht die Verfügbarkeit (eine zentralisierte Komponente kann ausfallen und das gesamte System lahmlegen) garantiert. + +Die Anwendung, die wir hier schreiben, ist eine Bank, die die Privatsphäre wahrt. Verschiedene Adressen haben Konten mit Guthaben, und sie können Geld (ETH) an andere Konten senden. Die Bank veröffentlicht Hashes des Zustands (Konten und deren Guthaben) und Transaktionen, hält aber die tatsächlichen Guthaben offchain, wo sie privat bleiben können. + +## Design {#design} + +Dies ist kein produktionsreifes System, sondern ein Lehrmittel. Als solches ist es mit mehreren vereinfachenden Annahmen geschrieben. + +- Fester Konten-Pool. Es gibt eine bestimmte Anzahl von Konten, und jedes Konto gehört zu einer vorbestimmten Adresse. Dies sorgt für ein viel einfacheres System, da es schwierig ist, Datenstrukturen variabler Größe in Zero-Knowledge-Proofs zu handhaben. Für ein produktionsreifes System können wir die [Merkle-Root](/developers/tutorials/merkle-proofs-for-offline-data-integrity/) als Zustands-Hash verwenden und Merkle-Proofs für die erforderlichen Guthaben bereitstellen. + +- Arbeitsspeicher. Auf einem Produktionssystem müssen wir alle Kontoguthaben auf die Festplatte schreiben, um sie im Falle eines Neustarts zu erhalten. Hier ist es in Ordnung, wenn die Informationen einfach verloren gehen. + +- Nur Überweisungen. Ein Produktionssystem würde eine Möglichkeit erfordern, Vermögenswerte bei der Bank einzuzahlen und abzuheben. Aber der Zweck hier ist nur, das Konzept zu veranschaulichen, daher ist diese Bank auf Überweisungen beschränkt. + +### Zero-Knowledge-Proofs {#zero-knowledge-proofs} + +Auf einer fundamentalen Ebene zeigt ein Zero-Knowledge-Proof, dass der Beweisführer einige Daten, _Datenprivat_, kennt, sodass eine Beziehung _Beziehung_ zwischen einigen öffentlichen Daten, _Datenöffentlich_, und _Datenprivat_ besteht. Der Verifizierer kennt _Beziehung_ und _Datenöffentlich_. + +Um die Privatsphäre zu wahren, müssen die Zustände und die Transaktionen privat sein. Aber um die Integrität zu gewährleisten, muss der [kryptographische Hash](https://de.wikipedia.org/wiki/Kryptographische_Hashfunktion) der Zustände öffentlich sein. Um den Leuten, die Transaktionen einreichen, zu beweisen, dass diese Transaktionen wirklich stattgefunden haben, müssen wir auch Transaktions-Hashes veröffentlichen. + +In den meisten Fällen sind _Datenprivat_ die Eingabe für das Zero-Knowledge-Proof-Programm und _Datenöffentlich_ die Ausgabe. + +Diese Felder in _Datenprivat_: + +- _Zustandn_, der alte Zustand +- _Zustandn+1_, der neue Zustand +- _Transaktion_, eine Transaktion, die den alten Zustand in den neuen ändert. Diese Transaktion muss diese Felder enthalten: + - _Zieladresse_, die die Überweisung empfängt + - _Betrag_, der überwiesen wird + - _Nonce_, um sicherzustellen, dass jede Transaktion nur einmal verarbeitet werden kann. + Die Quelladresse muss nicht in der Transaktion enthalten sein, da sie aus der Signatur wiederhergestellt werden kann. +- _Signatur_, eine Signatur, die zur Durchführung der Transaktion berechtigt ist. In unserem Fall ist die einzige Adresse, die zur Durchführung einer Transaktion berechtigt ist, die Quelladresse. Da unser Zero-Knowledge-System so funktioniert, wie es funktioniert, benötigen wir zusätzlich zur Ethereum-Signatur auch den öffentlichen Schlüssel des Kontos. + +Dies sind die Felder in _Datenöffentlich_: + +- _Hash(Zustandn)_, der Hash des alten Zustands +- _Hash(Zustandn+1)_, der Hash des neuen Zustands +- _Hash(Transaktion)_, der Hash der Transaktion, der den Zustand von _Zustandn_ zu _Zustandn+1_ ändert. + +Die Beziehung überprüft mehrere Bedingungen: + +- Die öffentlichen Hashes sind tatsächlich die korrekten Hashes für die privaten Felder. +- Die Transaktion, angewendet auf den alten Zustand, ergibt den neuen Zustand. +- Die Signatur stammt von der Quelladresse der Transaktion. + +Aufgrund der Eigenschaften von kryptographischen Hash-Funktionen ist der Beweis dieser Bedingungen ausreichend, um die Integrität zu gewährleisten. + +### Datenstrukturen {#data-structures} + +Die primäre Datenstruktur ist der Zustand, der vom Server gehalten wird. Für jedes Konto verfolgt der Server das Kontoguthaben und eine [Nonce](https://de.wikipedia.org/wiki/Nonce), die verwendet wird, um [Replay-Angriffe](https://de.wikipedia.org/wiki/Replay-Angriff) zu verhindern. + +### Komponenten {#components} + +Dieses System erfordert zwei Komponenten: + +- Der _Server_, der Transaktionen empfängt, verarbeitet und Hashes zusammen mit den Zero-Knowledge-Proofs in der Chain postet. +- Ein _Smart Contract_, der die Hashes speichert und die Zero-Knowledge-Proofs verifiziert, um sicherzustellen, dass die Zustandsübergänge legitim sind. + +### Daten- und Kontrollfluss {#flows} + +Dies sind die Wege, auf denen die verschiedenen Komponenten kommunizieren, um von einem Konto auf ein anderes zu überweisen. + +1. Ein Webbrowser übermittelt eine signierte Transaktion, die eine Überweisung vom Konto des Unterzeichners auf ein anderes Konto anfordert. + +2. Der Server überprüft, ob die Transaktion gültig ist: + + - Der Unterzeichner hat ein Konto bei der Bank mit ausreichendem Guthaben. + - Der Empfänger hat ein Konto bei der Bank. + +3. Der Server berechnet den neuen Zustand, indem er den überwiesenen Betrag vom Guthaben des Unterzeichners abzieht und zum Guthaben des Empfängers addiert. + +4. Der Server berechnet einen Zero-Knowledge-Proof, dass die Zustandsänderung gültig ist. + +5. Der Server übermittelt eine Transaktion an Ethereum, die Folgendes enthält: + + - Der neue Zustands-Hash + - Der Transaktions-Hash (damit der Absender der Transaktion weiß, dass sie verarbeitet wurde) + - Der Zero-Knowledge-Proof, der beweist, dass der Übergang zum neuen Zustand gültig ist + +6. Der Smart Contract verifiziert den Zero-Knowledge-Proof. + +7. Wenn der Zero-Knowledge-Proof erfolgreich ist, führt der Smart Contract die folgenden Aktionen aus: + - Aktualisieren des aktuellen Zustands-Hashes auf den neuen Zustands-Hash + - Ausgabe eines Log-Eintrags mit dem neuen Zustands-Hash und dem Transaktions-Hash + +### Werkzeuge {#tools} + +Für den clientseitigen Code werden wir [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) und [Wagmi](https://wagmi.sh/) verwenden. Dies sind branchenübliche Werkzeuge; wenn Sie mit ihnen nicht vertraut sind, können Sie [dieses Tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/) verwenden. + +Der Großteil des Servers ist in JavaScript mit [Node](https://nodejs.org/en) geschrieben. Der Zero-Knowledge-Teil ist in [Noir](https://noir-lang.org/) geschrieben. Wir benötigen die Version `1.0.0-beta.10`, also führen Sie nach der [Installation von Noir gemäß den Anweisungen](https://noir-lang.org/docs/getting_started/quick_start) Folgendes aus: + +``` +noirup -v 1.0.0-beta.10 +``` + +Die Blockchain, die wir verwenden, ist `anvil`, eine lokale Test-Blockchain, die Teil von [Foundry](https://getfoundry.sh/introduction/installation) ist. + +## Implementierung {#implementation} + +Da es sich um ein komplexes System handelt, werden wir es in Etappen implementieren. + +### Stufe 1 – Manuelles Zero-Knowledge {#stage-1} + +Für die erste Stufe signieren wir eine Transaktion im Browser und geben dann die Informationen manuell an den Zero-Knowledge-Proof weiter. Der Zero-Knowledge-Code erwartet diese Informationen in `server/noir/Prover.toml` (dokumentiert [hier](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1)). + +So sehen Sie es in Aktion: + +1. Stellen Sie sicher, dass Sie [Node](https://nodejs.org/en/download) und [Noir](https://noir-lang.org/install) installiert haben. Installieren Sie sie vorzugsweise auf einem UNIX-System wie macOS, Linux oder [WSL](https://learn.microsoft.com/de-de/windows/wsl/install). + +2. Laden Sie den Code für Stufe 1 herunter und starten Sie den Webserver, um den Client-Code bereitzustellen. + + ```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 + ``` + + Der Grund, warum Sie hier einen Webserver benötigen, ist, dass viele Wallets (wie MetaMask) zur Verhinderung bestimmter Betrugsarten keine Dateien akzeptieren, die direkt von der Festplatte bereitgestellt werden. + +3. Öffnen Sie einen Browser mit einer Wallet. + +4. Geben Sie in der Wallet eine neue Passphrase ein. Beachten Sie, dass dies Ihre bestehende Passphrase löscht, also _stellen Sie sicher, dass Sie ein Backup haben_. + + Die Passphrase lautet `test test test test test test test test test test test junk`, die Standard-Test-Passphrase für Anvil. + +5. Navigieren Sie zum [clientseitigen Code](http://localhost:5173/). + +6. Verbinden Sie sich mit der Wallet und wählen Sie Ihr Zielkonto und den Betrag aus. + +7. Klicken Sie auf **Sign** und signieren Sie die Transaktion. + +8. Unter der Überschrift **Prover.toml** finden Sie einen Text. Ersetzen Sie `server/noir/Prover.toml` durch diesen Text. + +9. Führen Sie den Zero-Knowledge-Proof aus. + + ```sh + cd ../server/noir + nargo execute + ``` + + Die Ausgabe sollte ähnlich sein wie + + ``` + ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute + + [zkBank] Circuit witness successfully solved + [zkBank] Witness saved to target/zkBank.gz + [zkBank] Circuit output: (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85) + ``` + +10. Vergleichen Sie die letzten beiden Werte mit dem Hash, den Sie im Webbrowser sehen, um zu prüfen, ob die Nachricht korrekt gehasht wurde. + +#### `server/noir/Prover.toml` {#server-noir-prover-toml} + +[Diese Datei](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) zeigt das von Noir erwartete Informationsformat. + +```toml +message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0 " +``` + +Die Nachricht ist im Textformat, was es für den Benutzer leicht verständlich macht (was beim Signieren notwendig ist) und für den Noir-Code leicht zu parsen ist. Der Betrag wird in Finneys angegeben, um einerseits Teilüberweisungen zu ermöglichen und andererseits leicht lesbar zu sein. Die letzte Zahl ist die [Nonce](https://de.wikipedia.org/wiki/Nonce). + +Die Zeichenkette ist 100 Zeichen lang. Zero-Knowledge-Proofs können nicht gut mit Daten variabler Größe umgehen, daher ist es oft notwendig, Daten aufzufüllen. + +```toml +pubKeyX=["0x83",...,"0x75"] +pubKeyY=["0x35",...,"0xa5"] +signature=["0xb1",...,"0x0d"] +``` + +Diese drei Parameter sind Byte-Arrays fester Größe. + +```toml +[[accounts]] +address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance=100_000 +nonce=0 + +[[accounts]] +address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +balance=100_000 +nonce=0 +``` + +Dies ist die Art, ein Array von Strukturen zu spezifizieren. Für jeden Eintrag geben wir die Adresse, das Guthaben (in milliETH, auch bekannt als [Finney](https://cryptovalleyjournal.com/glossary/finney/)) und den nächsten Nonce-Wert an. + +#### `client/src/Transfer.tsx` {#client-src-transfer-tsx} + +[Diese Datei](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx) implementiert die clientseitige Verarbeitung und generiert die `server/noir/Prover.toml`-Datei (diejenige, die die Zero-Knowledge-Parameter enthält). + +Hier ist die Erklärung der interessanteren Teile. + +```tsx +export default attrs => { +``` + +Diese Funktion erstellt die `Transfer`-React-Komponente, die andere Dateien importieren können. + +```tsx + const accounts = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + ] +``` + +Dies sind die Kontoadressen, die durch die Passphrase `test ...` erstellt werden. test junk` Passphrase. Wenn Sie Ihre eigenen Adressen verwenden möchten, ändern Sie einfach diese Definition. + +```tsx + const account = useAccount() + const wallet = createWalletClient({ + transport: custom(window.ethereum!) + }) +``` + +Diese [Wagmi-Hooks](https://wagmi.sh/react/api/hooks) ermöglichen uns den Zugriff auf die [viem](https://viem.sh/)-Bibliothek und die Wallet. + +```tsx + const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ") +``` + +Dies ist die mit Leerzeichen aufgefüllte Nachricht. Jedes Mal, wenn sich eine der `useState`-Variablen (https://react.dev/reference/react/useState) ändert, wird die Komponente neu gezeichnet und die `message` wird aktualisiert. + +```tsx + const sign = async () => { +``` + +Diese Funktion wird aufgerufen, wenn der Benutzer auf die Schaltfläche **Sign** klickt. Die Nachricht wird automatisch aktualisiert, aber die Signatur erfordert die Genehmigung des Benutzers in der Wallet, und wir möchten nicht danach fragen, es sei denn, es ist notwendig. + +```tsx + const signature = await wallet.signMessage({ + account: fromAccount, + message, + }) +``` + +Bitten Sie die Wallet, [die Nachricht zu signieren](https://viem.sh/docs/accounts/local/signMessage). + +```tsx + const hash = hashMessage(message) +``` + +Holen Sie sich den Nachrichten-Hash. Es ist hilfreich, ihn dem Benutzer für das Debugging (des Noir-Codes) zur Verfügung zu stellen. + +```tsx + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +[Holen Sie sich den öffentlichen Schlüssel](https://viem.sh/docs/utilities/recoverPublicKey). Dies ist für die [Noir `ecrecover`](https://github.com/colinnielsen/ecrecover-noir)-Funktion erforderlich. + +```tsx + setSignature(signature) + setHash(hash) + setPubKey(pubKey) +``` + +Setzen Sie die Zustandvariablen. Dadurch wird die Komponente neu gezeichnet (nachdem die `sign`-Funktion beendet ist) und dem Benutzer die aktualisierten Werte angezeigt. + +```tsx + let proverToml = ` +``` + +Der Text für `Prover.toml`. + +```tsx +message="${message}" + +pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))} +pubKeyY=${hexToArray(pubKey.slice(4+2*32))} +``` + +Viem stellt uns den öffentlichen Schlüssel als 65-Byte-Hexadezimalzeichenkette zur Verfügung. Das erste Byte ist `0x04`, ein Versionsmarker. Darauf folgen 32 Bytes für das `x` des öffentlichen Schlüssels und dann 32 Bytes für das `y` des öffentlichen Schlüssels. + +Noir erwartet jedoch, diese Informationen als zwei Byte-Arrays zu erhalten, eines für `x` und eines für `y`. Es ist einfacher, dies hier auf dem Client zu parsen als als Teil des Zero-Knowledge-Proofs. + +Beachten Sie, dass dies im Allgemeinen eine gute Praxis bei Zero-Knowledge ist. Code innerhalb eines Zero-Knowledge-Proofs ist teuer, daher sollte jede Verarbeitung, die außerhalb des Zero-Knowledge-Proofs durchgeführt werden kann, auch außerhalb des Zero-Knowledge-Proofs durchgeführt werden. + +```tsx +signature=${hexToArray(signature.slice(2,-2))} +``` + +Die Signatur wird ebenfalls als eine 65-Byte lange Hexadezimal-Zeichenkette bereitgestellt. Das letzte Byte ist jedoch nur notwendig, um den öffentlichen Schlüssel wiederherzustellen. Da der öffentliche Schlüssel bereits dem Noir-Code zur Verfügung gestellt wird, benötigen wir ihn nicht zur Überprüfung der Signatur, und der Noir-Code erfordert ihn nicht. + +```tsx +${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")} +` +``` + +Stellen Sie die Konten bereit. + +```tsx + setProverToml(proverToml) + } + + return ( + <> +

Transfer

+``` + +Dies ist das HTML (genauer gesagt, [JSX](https://react.dev/learn/writing-markup-with-jsx)) Format der Komponente. + +#### `server/noir/src/main.nr` {#server-noir-src-main-nr} + +[Diese Datei](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr) ist der eigentliche Zero-Knowledge-Code. + +``` +use std::hash::pedersen_hash; +``` + +[Pedersen-Hash](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/) wird mit der [Noir-Standardbibliothek](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash) bereitgestellt. Zero-Knowledge-Proofs verwenden häufig diese Hash-Funktion. Es ist viel einfacher, sie in [arithmetischen Schaltungen](https://rareskills.io/post/arithmetic-circuit) im Vergleich zu den Standard-Hash-Funktionen zu berechnen. + +``` +use keccak256::keccak256; +use dep::ecrecover; +``` + +Diese beiden Funktionen sind externe Bibliotheken, die in [`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml) definiert sind. Sie sind genau das, wofür sie benannt sind: eine Funktion, die den [Keccak256-Hash](https://emn178.github.io/online-tools/keccak_256.html) berechnet, und eine Funktion, die Ethereum-Signaturen verifiziert und die Ethereum-Adresse des Unterzeichners wiederherstellt. + +``` +global ACCOUNT_NUMBER : u32 = 5; +``` + +Noir ist von [Rust](https://www.rust-lang.org/) inspiriert. Variablen sind standardmäßig Konstanten. Auf diese Weise definieren wir globale Konfigurationskonstanten. Insbesondere ist `ACCOUNT_NUMBER` die Anzahl der Konten, die wir speichern. + +Datentypen mit dem Namen `u` sind diese Anzahl von Bits, ohne Vorzeichen. Die einzigen unterstützten Typen sind `u8`, `u16`, `u32`, `u64` und `u128`. + +``` +global FLAT_ACCOUNT_FIELDS : u32 = 2; +``` + +Diese Variable wird für den Pedersen-Hash der Konten verwendet, wie unten erklärt. + +``` +global MESSAGE_LENGTH : u32 = 100; +``` + +Wie oben erklärt, ist die Nachrichtenlänge fest. Sie wird hier angegeben. + +``` +global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30]; +global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH; +``` + +[EIP-191-Signaturen](https://eips.ethereum.org/EIPS/eip-191) erfordern einen Puffer mit einem 26-Byte-Präfix, gefolgt von der Nachrichtenlänge in ASCII und schließlich der Nachricht selbst. + +``` +struct Account { + balance: u128, + address: Field, + nonce: u32, +} +``` + +Die Informationen, die wir über ein Konto speichern. [`Field`](https://noir-lang.org/docs/noir/concepts/data_types/fields) ist eine Zahl, typischerweise bis zu 253 Bits, die direkt in der [arithmetischen Schaltung](https://rareskills.io/post/arithmetic-circuit) verwendet werden kann, die den Zero-Knowledge-Proof implementiert. Hier verwenden wir das `Field`, um eine 160-Bit-Ethereum-Adresse zu speichern. + +``` +struct TransferTxn { + from: Field, + to: Field, + amount: u128, + nonce: u32 +} +``` + +Die Informationen, die wir für eine Überweisungstransaktion speichern. + +``` +fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] { +``` + +Eine Funktionsdefinition. Der Parameter ist eine `Account`-Information. Das Ergebnis ist ein Array von `Field`-Variablen, dessen Länge `FLAT_ACCOUNT_FIELDS` ist. + +``` + let flat = [ + account.address, + ((account.balance << 32) + account.nonce.into()).into(), + ]; +``` + +Der erste Wert im Array ist die Kontoadresse. Der zweite Wert enthält sowohl das Guthaben als auch die Nonce. Die `.into()`-Aufrufe ändern eine Zahl in den Datentyp, den sie haben muss. `account.nonce` ist ein `u32`-Wert, aber um ihn zu `account.balance « 32`, einem `u128`-Wert, hinzuzufügen, muss er ein `u128` sein. Das ist das erste `.into()`. Der zweite wandelt das `u128`-Ergebnis in ein `Field` um, damit es in das Array passt. + +``` + flat +} +``` + +In Noir können Funktionen nur am Ende einen Wert zurückgeben (es gibt keine vorzeitige Rückgabe). Um den Rückgabewert anzugeben, werten Sie ihn kurz vor der schließenden Klammer der Funktion aus. + +``` +fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] { +``` + +Diese Funktion wandelt das Konten-Array in ein `Field`-Array um, das als Eingabe für einen Pedersen-Hash verwendet werden kann. + +``` + let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER]; +``` + +So geben Sie eine veränderliche Variable an, d. h. _keine_ Konstante. Variablen in Noir müssen immer einen Wert haben, also initialisieren wir diese Variable mit Nullen. + +``` + for i in 0..ACCOUNT_NUMBER { +``` + +Dies ist eine `for`-Schleife. Beachten Sie, dass die Grenzen Konstanten sind. Noir-Schleifen müssen ihre Grenzen zur Kompilierzeit kennen. Der Grund dafür ist, dass arithmetische Schaltungen keine Flusskontrolle unterstützen. Bei der Verarbeitung einer `for`-Schleife fügt der Compiler den Code einfach mehrmals ein, einmal für jede Iteration. + +``` + 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)) +} +``` + +Schließlich sind wir bei der Funktion angelangt, die das Konten-Array hasht. + +``` +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; + } + } +``` + +Diese Funktion findet das Konto mit einer bestimmten Adresse. Diese Funktion wäre in Standardcode furchtbar ineffizient, da sie über alle Konten iteriert, auch nachdem sie die Adresse gefunden hat. + +Bei Zero-Knowledge-Proofs gibt es jedoch keine Flusskontrolle. Wenn wir jemals eine Bedingung prüfen müssen, müssen wir sie jedes Mal prüfen. + +Etwas Ähnliches geschieht mit `if`-Anweisungen. Die `if`-Anweisung in der obigen Schleife wird in diese mathematischen Aussagen übersetzt. + +_bedingungergebnis = accounts[i].address == address_ // eins, wenn sie gleich sind, sonst null + +_kontoneu = bedingungergebnis\*i + (1-bedingungergebnis)\*kontoalt_ + +```rust + assert (account < ACCOUNT_NUMBER, f"{address} hat kein Konto"); + + account +} +``` + +Die [`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert)-Funktion führt zum Absturz des Zero-Knowledge-Proofs, wenn die Behauptung falsch ist. In diesem Fall, wenn wir kein Konto mit der relevanten Adresse finden können. Um die Adresse zu melden, verwenden wir einen [Format-String](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] { +``` + +Diese Funktion wendet eine Überweisungstransaktion an und gibt das neue Kontenarray zurück. + +```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); +``` + +Wir können in Noir nicht auf Strukturelemente innerhalb eines Format-Strings zugreifen, also erstellen wir eine nutzbare Kopie. + +```rust + assert (accounts[from].balance >= txn.amount, + f"{txnFrom} hat keine {txnAmount} Finney"); + + assert (accounts[from].nonce == txn.nonce, + f"Transaktion hat Nonce {txnNonce}, aber das Konto wird voraussichtlich {accountNonce} verwenden"); +``` + +Dies sind zwei Bedingungen, die eine Transaktion ungültig machen könnten. + +```rust + let mut newAccounts = accounts; + + newAccounts[from].balance -= txn.amount; + newAccounts[from].nonce += 1; + newAccounts[to].balance += txn.amount; + + newAccounts +} +``` + +Erstellen Sie das neue Konten-Array und geben Sie es dann zurück. + +```rust +fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field +``` + +Diese Funktion liest die Adresse aus der Nachricht. + +```rust +{ + let mut result : Field = 0; + + for i in 7..47 { +``` + +Die Adresse ist immer 20 Byte (auch bekannt als 40 Hexadezimalziffern) lang und beginnt bei Zeichen #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) +``` + +Lesen Sie den Betrag und die Nonce aus der Nachricht. + +```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; +``` + +In der Nachricht ist die erste Zahl nach der Adresse der Betrag in Finney (auch bekannt als Tausendstel eines ETH), der überwiesen werden soll. Die zweite Zahl ist die Nonce. Jeder Text dazwischen wird ignoriert. + +```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 { // We just found it + stillReadingNonce = true; + lookingForNonce = false; + } + + if stillReadingNonce { + nonce = nonce*10 + digit.into(); + } + } else { + if stillReadingAmount { + stillReadingAmount = false; + lookingForNonce = true; + } + if stillReadingNonce { + stillReadingNonce = false; + } + } + } + + (amount, nonce) +} +``` + +Die Rückgabe eines [Tupels](https://noir-lang.org/docs/noir/concepts/data_types/tuples) ist die Noir-Methode, um mehrere Werte aus einer Funktion zurückzugeben. + +```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 +} +``` + +Diese Funktion konvertiert die Nachricht in Bytes und dann die Beträge in eine `TransferTxn`. + +```rust +// Das Äquivalent zu Viems hashMessage +// https://viem.sh/docs/utilities/hashMessage#hashmessage +fn hashMessage(message: str) -> [u8;32] { +``` + +Wir konnten den Pedersen-Hash für die Konten verwenden, da sie nur innerhalb des Zero-Knowledge-Proofs gehasht werden. In diesem Code müssen wir jedoch die Signatur der Nachricht überprüfen, die vom Browser generiert wird. Dafür müssen wir dem Ethereum-Signaturformat in [EIP 191](https://eips.ethereum.org/EIPS/eip-191) folgen. Das bedeutet, wir müssen einen kombinierten Puffer mit einem Standardpräfix, der Nachrichtenlänge in ASCII und der Nachricht selbst erstellen und den Ethereum-Standard Keccak256 verwenden, um ihn zu hashen. + +```rust + // ASCII-Präfix + 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' + ]; +``` + +Um Fälle zu vermeiden, in denen eine Anwendung den Benutzer bittet, eine Nachricht zu signieren, die als Transaktion oder für einen anderen Zweck verwendet werden kann, gibt EIP 191 an, dass alle signierten Nachrichten mit dem Zeichen 0x19 (kein gültiges ASCII-Zeichen) beginnen, gefolgt von `Ethereum Signed Message:` und einem Zeilenumbruch. + +```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, "Nachrichten, deren Länge drei Ziffern überschreitet, werden nicht unterstützt"); +``` + +Handhaben Sie Nachrichtenlängen bis zu 999 und brechen Sie ab, wenn sie größer ist. Ich habe diesen Code hinzugefügt, obwohl die Nachrichtenlänge eine Konstante ist, weil es die Änderung erleichtert. Auf einem Produktionssystem würden Sie wahrscheinlich einfach davon ausgehen, dass `MESSAGE_LENGTH` sich aus Leistungsgründen nicht ändert. + +```rust + keccak256::keccak256(buffer, HASH_BUFFER_SIZE) +} +``` + +Verwenden Sie die Ethereum-Standardfunktion `keccak256`. + +```rust +fn signatureToAddressAndHash( + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64] + ) -> (Field, Field, Field) // Adresse, erste 16 Bytes des Hash, letzte 16 Bytes des Hash +{ +``` + +Diese Funktion verifiziert die Signatur, was den Nachrichten-Hash erfordert. Es liefert uns dann die Adresse, die sie signiert hat, und den Nachrichten-Hash. Der Nachrichten-Hash wird in zwei `Field`-Werten geliefert, da diese im Rest des Programms einfacher zu verwenden sind als ein Byte-Array. + +Wir müssen zwei `Field`-Werte verwenden, da Feldberechnungen [modulo](https://de.wikipedia.org/wiki/Modular_arithmetic) einer großen Zahl durchgeführt werden, aber diese Zahl ist typischerweise kleiner als 256 Bits (andernfalls wäre es schwierig, diese Berechnungen in der EVM durchzuführen). + +```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(); + } +``` + +Spezifizieren Sie `hash1` und `hash2` als veränderliche Variablen und schreiben Sie den Hash Byte für Byte hinein. + +```rust + ( + ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), +``` + +Dies ist ähnlich wie [Soliditiys `ecrecover`](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions), mit zwei wichtigen Unterschieden: + +- Wenn die Signatur nicht gültig ist, schlägt der Aufruf ein `assert` fehl und das Programm wird abgebrochen. +- Obwohl der öffentliche Schlüssel aus der Signatur und dem Hash wiederhergestellt werden kann, handelt es sich um eine Verarbeitung, die extern erfolgen kann und daher nicht innerhalb des Zero-Knowledge-Proofs durchgeführt werden sollte. Wenn jemand versucht, uns hier zu betrügen, wird die Signaturüberprüfung fehlschlagen. + +```rust + hash1, + hash2 + ) +} + +fn main( + accounts: [Account; ACCOUNT_NUMBER], + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64], + ) -> pub ( + Field, // Hash des alten Konten-Arrays + Field, // Hash des neuen Konten-Arrays + Field, // Erste 16 Bytes des Nachrichten-Hashs + Field, // Letzte 16 Bytes des Nachrichten-Hashs + ) +``` + +Schließlich erreichen wir die `main`-Funktion. Wir müssen beweisen, dass wir eine Transaktion haben, die den Hash der Konten gültig vom alten Wert auf den neuen ändert. Wir müssen auch beweisen, dass sie diesen spezifischen Transaktions-Hash hat, damit die Person, die sie gesendet hat, weiß, dass ihre Transaktion verarbeitet wurde. + +```rust +{ + let mut txn = readTransferTxn(message); +``` + +Wir brauchen `txn`, um veränderbar zu sein, weil wir die Von-Adresse nicht aus der Nachricht, sondern aus der Signatur lesen. + +```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 + ) +} +``` + +### Stufe 2 – Hinzufügen eines Servers {#stage-2} + +In der zweiten Stufe fügen wir einen Server hinzu, der Überweisungstransaktionen vom Browser empfängt und implementiert. + +So sehen Sie es in Aktion: + +1. Stoppen Sie Vite, wenn es läuft. + +2. Laden Sie den Branch mit dem Server herunter und stellen Sie sicher, dass Sie alle erforderlichen Module haben. + + ```sh + git checkout 02-add-server + cd client + npm install + cd ../server + npm install + ``` + + Es ist nicht notwendig, den Noir-Code zu kompilieren, es ist derselbe Code, den Sie für Stufe 1 verwendet haben. + +3. Starten Sie den Server. + + ```sh + npm run start + ``` + +4. Führen Sie Vite in einem separaten Kommandozeilenfenster aus, um den Browser-Code bereitzustellen. + + ```sh + cd client + npm run dev + ``` + +5. Navigieren Sie zum Client-Code unter [http://localhost:5173](http://localhost:5173) + +6. Bevor Sie eine Transaktion ausgeben können, müssen Sie die Nonce sowie den Betrag kennen, den Sie senden können. Um diese Informationen zu erhalten, klicken Sie auf **Kontodaten aktualisieren** und signieren Sie die Nachricht. + + Wir haben hier ein Dilemma. Einerseits wollen wir keine Nachricht signieren, die wiederverwendet werden kann (ein [Replay-Angriff](https://de.wikipedia.org/wiki/Replay-Angriff)), weshalb wir überhaupt eine Nonce wollen. Allerdings haben wir noch keine Nonce. Die Lösung besteht darin, eine Nonce zu wählen, die nur einmal verwendet werden kann und die wir bereits auf beiden Seiten haben, wie z. B. die aktuelle Zeit. + + Das Problem bei dieser Lösung ist, dass die Zeit möglicherweise nicht perfekt synchronisiert ist. Stattdessen signieren wir einen Wert, der sich jede Minute ändert. Dies bedeutet, dass unser Verwundbarkeitsfenster für Replay-Angriffe höchstens eine Minute beträgt. In Anbetracht der Tatsache, dass die signierte Anfrage in der Produktion durch TLS geschützt wird und dass die andere Seite des Tunnels – der Server – das Guthaben und die Nonce bereits offenlegen kann (er muss sie kennen, um zu funktionieren), ist dies ein akzeptables Risiko. + +7. Sobald der Browser das Guthaben und die Nonce zurückerhält, zeigt er das Überweisungsformular an. Wählen Sie die Zieladresse und den Betrag aus und klicken Sie auf **Überweisen**. Signieren Sie diese Anfrage. + +8. Um die Überweisung zu sehen, **aktualisieren Sie die Kontodaten** oder schauen Sie in das Fenster, in dem Sie den Server ausführen. Der Server protokolliert den Zustand bei jeder Änderung. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 64000 (1) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 100000 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 56800 (2) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + Txn send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 processed + New state: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 has 53800 (3) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 has 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC has 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 has 139000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 has 100000 (0) + ``` + +#### `server/index.mjs` {#server-index-mjs-1} + +[Diese Datei](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs) enthält den Serverprozess und interagiert mit dem Noir-Code unter [`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr). Hier ist eine Erklärung der interessanten Teile. + +```js +import { Noir } from '@noir-lang/noir_js' +``` + +Die [noir.js](https://www.npmjs.com/package/@noir-lang/noir_js)-Bibliothek dient als Schnittstelle zwischen JavaScript-Code und Noir-Code. + +```js +const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json")) +const noir = new Noir(circuit) +``` + +Laden Sie die arithmetische Schaltung – das kompilierte Noir-Programm, das wir in der vorherigen Stufe erstellt haben – und bereiten Sie sich auf ihre Ausführung vor. + +```js +// Wir stellen Kontoinformationen nur als Antwort auf eine signierte Anfrage zur Verfügung +const accountInformation = async signature => { + const fromAddress = await recoverAddress({ + hash: hashMessage("Get account data " + Math.floor((new Date().getTime())/60000)), + signature + }) +``` + +Um Kontoinformationen bereitzustellen, benötigen wir nur die Signatur. Der Grund dafür ist, dass wir bereits wissen, was die Nachricht sein wird, und daher auch den Nachrichten-Hash. + +```js +const processMessage = async (message, signature) => { +``` + +Verarbeiten Sie eine Nachricht und führen Sie die darin kodierte Transaktion aus. + +```js + // Holen Sie sich den öffentlichen Schlüssel + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +Jetzt, da wir JavaScript auf dem Server ausführen, können wir den öffentlichen Schlüssel dort anstatt auf dem Client abrufen. + +```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` führt das Noir-Programm aus. Die Parameter entsprechen denen, die in [`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) angegeben sind. Beachten Sie, dass lange Werte als Array von Hexadezimal-Strings (`["0x60", "0xA7"]`) und nicht als einzelner Hexadezimalwert (`0x60A7`) angegeben werden, wie es Viem tut. + +```js + } catch (err) { + console.log(`Noir error: ${err}`) + throw Error("Invalid transaction, not processed") + } +``` + +Wenn ein Fehler auftritt, fangen Sie ihn ab und leiten Sie eine vereinfachte Version an den Client weiter. + +```js + Accounts[fromAccountNumber].nonce++ + Accounts[fromAccountNumber].balance -= amount + Accounts[toAccountNumber].balance += amount +``` + +Führen Sie die Transaktion aus. Wir haben dies bereits im Noir-Code getan, aber es ist einfacher, es hier erneut zu tun, als das Ergebnis von dort zu extrahieren. + +```js +let Accounts = [ + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + balance: 5000, + nonce: 0, + }, +``` + +Die ursprüngliche `Accounts`-Struktur. + +### Stufe 3 – Ethereum Smart Contracts {#stage-3} + +1. Stoppen Sie die Server- und Client-Prozesse. + +2. Laden Sie den Branch mit den Smart Contracts herunter und stellen Sie sicher, dass Sie alle erforderlichen Module haben. + + ```sh + git checkout 03-smart-contracts + cd client + npm install + cd ../server + npm install + ``` + +3. Führen Sie `anvil` in einem separaten Kommandozeilenfenster aus. + +4. Generieren Sie den Verifizierungsschlüssel und den Solidity-Verifizierer und kopieren Sie dann den Verifizierer-Code in das Solidity-Projekt. + + ```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. Gehen Sie zu den Smart Contracts und setzen Sie die Umgebungsvariablen, um die `anvil` Blockchain zu verwenden. + + ```sh + cd ../../smart-contracts + export ETH_RPC_URL=http://localhost:8545 + ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ``` + +6. Stellen Sie `Verifier.sol` bereit und speichern Sie die Adresse in einer Umgebungsvariable. + + ```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. Stellen Sie den `ZkBank`-Vertrag bereit. + + ```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 + ``` + + Der Wert `0x199..67b` ist der Pederson-Hash des Anfangszustands von `Accounts`. Wenn Sie diesen Anfangszustand in `server/index.mjs` ändern, können Sie eine Transaktion ausführen, um den vom Zero-Knowledge-Proof gemeldeten Anfangs-Hash zu sehen. + +8. Starten Sie den Server. + + ```sh + cd ../server + npm run start + ``` + +9. Führen Sie den Client in einem anderen Befehlszeilenfenster aus. + + ```sh + cd client + npm run dev + ``` + +10. Führen Sie einige Transaktionen aus. + +11. Um zu überprüfen, dass der Zustand onchain geändert wurde, starten Sie den Serverprozess neu. Beachten Sie, dass `ZkBank` keine Transaktionen mehr akzeptiert, da der ursprüngliche Hash-Wert in den Transaktionen vom onchain gespeicherten Hash-Wert abweicht. + + Dies ist die Art von Fehler, die erwartet wird. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Listening on port 3000 + Verification error: ContractFunctionExecutionError: The contract function "processTransaction" reverted with the following reason: + Wrong old state hash + + Contract Call: + address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + function: processTransaction(bytes _proof, bytes32[] _publicInputs) + args: (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000 + ``` + +#### `server/index.mjs` {#server-index-mjs-2} + +Die Änderungen in dieser Datei beziehen sich hauptsächlich auf die Erstellung des tatsächlichen Beweises und dessen Einreichung onchain. + +```js +import { exec } from 'child_process' +import util from 'util' + +const execPromise = util.promisify(exec) +``` + +Wir müssen [das Barretenberg-Paket](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg) verwenden, um den tatsächlichen Beweis zu erstellen, der onchain gesendet werden soll. Wir können dieses Paket entweder über die Befehlszeilenschnittstelle (`bb`) oder über die [JavaScript-Bibliothek `bb.js`](https://www.npmjs.com/package/@aztec/bb.js) verwenden. Die JavaScript-Bibliothek ist viel langsamer als nativer Code, daher verwenden wir hier [`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback), um die Befehlszeile zu nutzen. + +Beachten Sie, dass, wenn Sie sich für die Verwendung von `bb.js` entscheiden, Sie eine Version verwenden müssen, die mit der von Ihnen verwendeten Noir-Version kompatibel ist. Zum Zeitpunkt der Erstellung dieses Artikels verwendet die aktuelle Noir-Version (1.0.0-beta.11) `bb.js` Version 0.87. + +```js +const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +``` + +Die Adresse hier ist diejenige, die Sie erhalten, wenn Sie mit einem sauberen `anvil` beginnen und den obigen Anweisungen folgen. + +```js +const walletClient = createWalletClient({ + chain: anvil, + transport: http(), + account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") +}) +``` + +Dieser private Schlüssel ist einer der standardmäßigen, vorfinanzierten Konten in `anvil`. + +```js +const generateProof = async (witness, fileID) => { +``` + +Generieren Sie einen Beweis mit der ausführbaren Datei `bb`. + +```js + const fname = `witness-${fileID}.gz` + await fs.writeFile(fname, witness) +``` + +Schreiben Sie den Witness in eine Datei. + +```js + await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`) +``` + +Erstellen Sie tatsächlich den Beweis. Dieser Schritt erstellt auch eine Datei mit den öffentlichen Variablen, aber die brauchen wir nicht. Diese Variablen haben wir bereits von `noir.execute` erhalten. + +```js + const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "") +``` + +Der Beweis ist ein JSON-Array von `Field`-Werten, die jeweils als Hexadezimalwert dargestellt werden. Wir müssen es jedoch in der Transaktion als einen einzigen `bytes`-Wert senden, den Viem durch eine große Hexadezimalzeichenkette darstellt. Hier ändern wir das Format, indem wir alle Werte verketten, alle `0x` entfernen und dann am Ende eines hinzufügen. + +```js + await execPromise(`rm -r ${fname} ${fileID}`) + + return proof +} +``` + +Aufräumen und den Beweis zurückgeben. + +```js +const processMessage = async (message, signature) => { + . + . + . + + const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0")) +``` + +Die öffentlichen Felder müssen ein Array von 32-Byte-Werten sein. Da wir jedoch den Transaktions-Hash zwischen zwei `Field`-Werten aufteilen mussten, erscheint er als 16-Byte-Wert. Hier fügen wir Nullen hinzu, damit Viem versteht, dass es sich tatsächlich um 32 Bytes handelt. + +```js + const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`) +``` + +Jede Adresse verwendet jede Nonce nur einmal, sodass wir eine Kombination aus `fromAddress` und `nonce` als eindeutigen Bezeichner für die Witness-Datei und das Ausgabeverzeichnis verwenden können. + +```js + try { + await zkBank.write.processTransaction([ + proof, publicFields]) + } catch (err) { + console.log(`Verification error: ${err}`) + throw Error("Can't verify the transaction onchain") + } + . + . + . +} +``` + +Senden Sie die Transaktion an die Chain. + +#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol} + +Dies ist der Onchain-Code, der die Transaktion empfängt. + +```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); + } +``` + +Der Onchain-Code muss zwei Variablen nachverfolgen: den Verifizierer (ein separater Vertrag, der von `nargo` erstellt wird) und den aktuellen Zustands-Hash. + +```solidity + event TransactionProcessed( + bytes32 indexed transactionHash, + bytes32 oldStateHash, + bytes32 newStateHash + ); +``` + +Jedes Mal, wenn sich der Zustand ändert, geben wir ein `TransactionProcessed`-Ereignis aus. + +```solidity + function processTransaction( + bytes calldata _proof, + bytes32[] calldata _publicFields + ) public { +``` + +Diese Funktion verarbeitet Transaktionen. Sie erhält den Beweis (als `bytes`) und die öffentlichen Eingaben (als `bytes32`-Array) in dem Format, das der Verifizierer benötigt (um die Onchain-Verarbeitung und damit die Gas-Kosten zu minimieren). + +```solidity + require(_publicInputs[0] == currentStateHash, + "Falscher alter Zustands-Hash"); +``` + +Der Zero-Knowledge-Proof muss beweisen, dass die Transaktion von unserem aktuellen Hash zu einem neuen wechselt. + +```solidity + myVerifier.verify(_proof, _publicFields); +``` + +Rufen Sie den Verifizierer-Vertrag auf, um den Zero-Knowledge-Proof zu verifizieren. Dieser Schritt macht die Transaktion rückgängig, wenn der Zero-Knowledge-Proof falsch ist. + +```solidity + currentStateHash = _publicFields[1]; + + emit TransactionProcessed( + _publicFields[2]<<128 | _publicFields[3], + _publicFields[0], + _publicFields[1] + ); + } +} +``` + +Wenn alles stimmt, aktualisieren Sie den Zustands-Hash auf den neuen Wert und geben Sie ein `TransactionProcessed`-Ereignis aus. + +## Missbrauch durch die zentralisierte Komponente {#abuses} + +Informationssicherheit besteht aus drei Attributen: + +- _Vertraulichkeit_: Benutzer können keine Informationen lesen, für deren Lektüre sie nicht autorisiert sind. +- _Integrität_: Informationen können nur von autorisierten Benutzern auf autorisierte Weise geändert werden. +- _Verfügbarkeit_: Autorisierte Benutzer können das System verwenden. + +Auf diesem System wird die Integrität durch Zero-Knowledge-Proofs gewährleistet. Verfügbarkeit ist viel schwerer zu garantieren, und Vertraulichkeit ist unmöglich, da die Bank das Guthaben jedes Kontos und alle Transaktionen kennen muss. Es gibt keine Möglichkeit, eine Entität, die über Informationen verfügt, daran zu hindern, diese Informationen weiterzugeben. + +Es könnte möglich sein, eine wirklich vertrauliche Bank mit [Stealth-Adressen](https://vitalik.eth.limo/general/2023/01/20/stealth.html) zu erstellen, aber das liegt außerhalb des Rahmens dieses Artikels. + +### Falsche Informationen {#false-info} + +Eine Möglichkeit, wie der Server die Integrität verletzen kann, ist die Bereitstellung falscher Informationen, wenn [Daten angefordert werden](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291). + +Um dies zu lösen, können wir ein zweites Noir-Programm schreiben, das die Konten als private Eingabe und die Adresse, für die Informationen angefordert werden, als öffentliche Eingabe erhält. Die Ausgabe sind das Guthaben und die Nonce dieser Adresse sowie der Hash der Konten. + +Natürlich kann dieser Beweis nicht onchain verifiziert werden, da wir keine Nonces und Guthaben onchain veröffentlichen wollen. Er kann jedoch vom Client-Code, der im Browser ausgeführt wird, verifiziert werden. + +### Erzwungene Transaktionen {#forced-txns} + +Der übliche Mechanismus zur Gewährleistung der Verfügbarkeit und zur Verhinderung von Zensur auf L2s sind [erzwungene Transaktionen](https://docs.optimism.io/stack/transactions/forced-transaction). Aber erzwungene Transaktionen lassen sich nicht mit Zero-Knowledge-Proofs kombinieren. Der Server ist die einzige Instanz, die Transaktionen überprüfen kann. + +Wir können `smart-contracts/src/ZkBank.sol` so ändern, dass er erzwungene Transaktionen akzeptiert und den Server daran hindert, den Zustand zu ändern, bis sie verarbeitet sind. Dies eröffnet uns jedoch einen einfachen Denial-of-Service-Angriff. Was ist, wenn eine erzwungene Transaktion ungültig ist und daher unmöglich zu verarbeiten ist? + +Die Lösung ist ein Zero-Knowledge-Proof, dass eine erzwungene Transaktion ungültig ist. Dies gibt dem Server drei Optionen: + +- Verarbeiten Sie die erzwungene Transaktion und stellen Sie einen Zero-Knowledge-Proof bereit, dass sie verarbeitet wurde und den neuen Zustands-Hash. +- Lehnen Sie die erzwungene Transaktion ab und legen Sie dem Vertrag einen Zero-Knowledge-Proof vor, dass die Transaktion ungültig ist (unbekannte Adresse, falsche Nonce oder unzureichendes Guthaben). +- Ignorieren Sie die erzwungene Transaktion. Es gibt keine Möglichkeit, den Server zu zwingen, die Transaktion tatsächlich zu verarbeiten, aber es bedeutet, dass das gesamte System nicht verfügbar ist. + +#### Verfügbarkeitsanleihen {#avail-bonds} + +In einer realen Implementierung gäbe es wahrscheinlich eine Art Profitmotiv, den Server am Laufen zu halten. Wir können diesen Anreiz verstärken, indem der Server eine Verfügbarkeitsanleihe hinterlegt, die jeder verbrennen kann, wenn eine erzwungene Transaktion nicht innerhalb eines bestimmten Zeitraums verarbeitet wird. + +### Schlechter Noir-Code {#bad-noir-code} + +Normalerweise, um das Vertrauen der Leute in einen Smart Contract zu gewinnen, laden wir den Quellcode auf einen [Block-Explorer](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract) hoch. Im Falle von Zero-Knowledge-Proofs ist das jedoch unzureichend. + +`Verifier.sol` enthält den Verifizierungsschlüssel, der eine Funktion des Noir-Programms ist. Dieser Schlüssel sagt uns jedoch nicht, was das Noir-Programm war. Um tatsächlich eine vertrauenswürdige Lösung zu haben, müssen Sie das Noir-Programm (und die Version, die es erstellt hat) hochladen. Andernfalls könnten die Zero-Knowledge-Proofs ein anderes Programm widerspiegeln, eines mit einer Hintertür. + +Bis Block-Explorer es uns ermöglichen, Noir-Programme hochzuladen und zu verifizieren, sollten Sie es selbst tun (vorzugsweise auf [IPFS](/developers/tutorials/ipfs-decentralized-ui/)). Dann können versierte Benutzer den Quellcode herunterladen, selbst kompilieren, `Verifier.sol` erstellen und überprüfen, ob er mit dem auf der Chain identisch ist. + +## Fazit {#conclusion} + +Plasma-Anwendungen erfordern eine zentralisierte Komponente als Informationsspeicher. Dies eröffnet potenzielle Schwachstellen, ermöglicht es uns aber im Gegenzug, die Privatsphäre auf eine Weise zu wahren, die auf der Blockchain selbst nicht verfügbar ist. Mit Zero-Knowledge-Proofs können wir die Integrität gewährleisten und es möglicherweise wirtschaftlich vorteilhaft machen, dass der Betreiber der zentralisierten Komponente die Verfügbarkeit aufrechterhält. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). + +## Anerkennungen {#acknowledgements} + +- Josh Crites hat einen Entwurf dieses Artikels gelesen und mir bei einem kniffligen Noir-Problem geholfen. + +Alle verbleibenden Fehler liegen in meiner Verantwortung. diff --git a/public/content/translations/de/developers/tutorials/calling-a-smart-contract-from-javascript/index.md b/public/content/translations/de/developers/tutorials/calling-a-smart-contract-from-javascript/index.md index 80de3796183..3f6f5e8c336 100644 --- a/public/content/translations/de/developers/tutorials/calling-a-smart-contract-from-javascript/index.md +++ b/public/content/translations/de/developers/tutorials/calling-a-smart-contract-from-javascript/index.md @@ -1,12 +1,8 @@ --- -title: Smart Contract aus JavaScript aufrufen -description: So rufen Sie am Beispiel eines Dai-Tokens eine Smart-Contract-Funktion von JavaScript aus auf +title: Aufruf eines Smart Contracts aus JavaScript +description: Wie Sie eine Smart-Contract-Funktion aus JavaScript am Beispiel eines Dai-Tokens aufrufen author: jdourlens -tags: - - "Transaktionen" - - "Frontend" - - "JavaScript" - - "web3.js" +tags: [ "Transaktionen", "Frontend", "JavaScript", "web3.js" ] skill: beginner lang: de published: 2020-04-19 @@ -23,7 +19,7 @@ Für dieses Beispiel wird ein DAI-Token verwendet. Zu Testzwecken werden wir die ganache-cli -f https://mainnet.infura.io/v3/[YOUR INFURA KEY] -d -i 66 1 --unlock 0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81 ``` -Für die Interaktoin mit einem Smart Contract benötigen wir seine Adresse und ABI: +Für die Interaktion mit einem Smart Contract benötigen wir seine Adresse und ABI: ```js const ERC20TransferABI = [ @@ -74,9 +70,9 @@ const ERC20TransferABI = [ const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f" ``` -Für dieses Projekt haben wir die komplette ERC20 ABI gestrippt, um nur die `balanceOf`- und `transfer`-Funktion zu behalten. Sie können [die komplette ERC20 ABI allerdings hier finden](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). +Für dieses Projekt haben wir die komplette ERC20-ABI auf die Funktionen `balanceOf` und `transfer` reduziert, aber [die vollständige ERC20-ABI finden Sie hier](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). -Anschließend müssen wir unseren Smart Contract starten: +Anschließend müssen wir unseren Smart Contract instanziieren: ```js const web3 = new Web3("http://localhost:8545") @@ -87,7 +83,7 @@ const daiToken = new web3.eth.Contract(ERC20TransferABI, DAI_ADDRESS) Außerdem richten wir zwei Adressen ein: - diejenige, die die Übertragung erhält und -- diejeige, die wir bereits eingerichtet haben, und die die Übertragung senden wird: +- diejenige, die wir bereits freigeschaltet haben und die sie senden wird: ```js const senderAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81" @@ -98,9 +94,9 @@ Im nächsten Teil rufen wir die Funktion `balanceOf` auf, um die aktuelle Menge ## Aufruf: Wert aus einem Smart Contract lesen {#call-reading-value-from-a-smart-contract} -Das erste Beispiel ruft eine "konstante" Methode auf und führt deren Smart-Contract-Methode im EVM aus, ohne eine Transaktion zu senden. Hierfür lesen wir den ERC20-Saldo einer Adresse aus. [Lesen Sie unseren Artikel über ERC20-Token](/developers/tutorials/understand-the-erc-20-token-smart-contract/). +Das erste Beispiel ruft eine „konstante“ Methode auf und führt deren Smart-Contract-Methode in der EVM aus, ohne eine Transaktion zu senden. Hierfür lesen wir den ERC20-Saldo einer Adresse aus. [Lesen Sie unseren Artikel über ERC20-Token](/developers/tutorials/understand-the-erc-20-token-smart-contract/). -Sie können auf die Methoden eines instanziierten Smart Contracts, für den Sie die ABI bereitgestellt haben, wie folgt zugreifen: `yourContract.methods.methodname`. Durch Verwendung der `Aufruf`-Funktion erhalten Sie das Ergebnis der Ausführung der Funktion. +Sie können auf die Methoden eines instanziierten Smart Contracts, für die Sie die ABI bereitgestellt haben, wie folgt zugreifen: `yourContract.methods.methodname`. Durch die Verwendung der `call`-Funktion erhalten Sie das Ergebnis der Ausführung der Funktion. ```js daiToken.methods.balanceOf(senderAddress).call(function (err, res) { @@ -112,11 +108,11 @@ daiToken.methods.balanceOf(senderAddress).call(function (err, res) { }) ``` -Denken Sie daran, dass DAI ERC20 18 Dezimalstellen hat. Das bedeutet, dass Sie 18 Nullen entfernen müssen, um den richtigen Betrag zu erhalten. uint256 werden als Strings zurückgegeben, da JavaScript keine großen numerischen Werte verarbeiten kann. Wenn Sie nicht sicher sind, [wie Sie mit großen Zahlen in JS umgehen sollen, lesen Sie unser Tutorial über bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). +Denken Sie daran, dass DAI ERC20 18 Dezimalstellen hat. Das bedeutet, dass Sie 18 Nullen entfernen müssen, um den richtigen Betrag zu erhalten. uint256 werden als Strings zurückgegeben, da JavaScript keine großen numerischen Werte verarbeiten kann. Wenn Sie sich nicht sicher sind, wie Sie mit großen Zahlen in JS umgehen sollen, [lesen Sie unser Tutorial über bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). -## Senden: Senden einer Transaktion an eine Smart-Contract-Funktion {#send-sending-a-transaction-to-a-smart-contract-function} +## Senden: Eine Transaktion an eine Smart-Contract-Funktion senden {#send-sending-a-transaction-to-a-smart-contract-function} -Für das zweite Beispiel rufen wir die Transferfunktion des DAI-Smart Contracts auf, um 10 DAI an unsere zweite Adresse zu senden. Die Übertragungsfunktion akzeptiert zwei Parameter: die Empfängeradresse und den Betrag des zu übertragenden Tokens: +Im zweiten Beispiel rufen wir die Transfer-Funktion des DAI-Smart-Contracts auf, um 10 DAI an unsere zweite Adresse zu senden. Die Übertragungsfunktion akzeptiert zwei Parameter: die Empfängeradresse und den Betrag der zu übertragenden Token: ```js daiToken.methods @@ -130,6 +126,6 @@ daiToken.methods }) ``` -Die Aufruffunktion gibt den Hash der Transaktion zurück, der in die Blockchain gemined wird. Bei Ethereum sind Transaktions-Hashes vorhersehbar – so können wir den Hash der Transaktion erhalten, bevor sie ausgeführt wird ([lernen Sie hier, wie Hashes berechnet werden](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). +Die Aufruffunktion gibt den Hash der Transaktion zurück, der in die Blockchain gemined wird. Auf Ethereum sind Transaktions-Hashes vorhersagbar – so können wir den Hash der Transaktion erhalten, bevor sie ausgeführt wird ([hier erfahren Sie, wie Hashes berechnet werden](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). -Da die Funktion die Transaktion nur an die Blockchain sendet, können wir das Ergebnis erst sehen, wenn es gemined und in die Blockchain aufgenommen wurde. Im nächsten Tutorial werden wir lernen, [wie man auf die Ausführung einer Transaktion auf der Blockchain wartet, indem man ihren Hash kennt](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). +Da die Funktion die Transaktion nur an die Blockchain sendet, können wir das Ergebnis erst sehen, wenn es gemined und in die Blockchain aufgenommen wurde. Im nächsten Tutorial lernen wir, [wie man anhand des Hashes einer Transaktion darauf wartet, dass sie auf der Blockchain ausgeführt wird](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). diff --git a/public/content/translations/de/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md b/public/content/translations/de/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md new file mode 100644 index 00000000000..6005c7e4797 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md @@ -0,0 +1,585 @@ +--- +title: "Erstellen einer Benutzeroberfläche für Ihren Vertrag" +description: "Mithilfe moderner Komponenten wie TypeScript, React, Vite und Wagmi werden wir eine moderne, aber minimalistische Benutzeroberfläche durchgehen und lernen, wie man eine Wallet mit der Benutzeroberfläche verbindet, einen Smart Contract aufruft, um Informationen auszulesen, eine Transaktion an einen Smart Contract zu senden und Ereignisse von einem Smart Contract zu überwachen, um Änderungen zu erkennen." +author: Ori Pomerantz +tags: [ "typescript", "react", "vite", "wagmi", "Frontend" ] +skill: beginner +published: 2023-11-01 +lang: de +sidebarDepth: 3 +--- + +Sie haben eine Funktion gefunden, die wir im Ethereum-Ökosystem benötigen. Sie haben die Smart Contracts geschrieben, um sie zu implementieren, und vielleicht sogar einen zugehörigen Code, der Offchain ausgeführt wird. Das ist großartig! Leider werden Sie ohne eine Benutzeroberfläche keine Benutzer haben, und als Sie das letzte Mal eine Website geschrieben haben, benutzten die Leute noch Wählmodems und JavaScript war neu. + +Dieser Artikel ist für Sie. Ich gehe davon aus, dass Sie programmieren können und vielleicht ein wenig JavaScript und HTML kennen, aber dass Ihre Kenntnisse im Bereich der Benutzeroberflächen eingerostet und veraltet sind. Gemeinsam werden wir eine einfache, moderne Anwendung durchgehen, damit Sie sehen, wie man das heutzutage macht. + +## Warum ist das wichtig? {#why-important} + +Theoretisch könnten Sie die Leute einfach [Etherscan](https://holesky.etherscan.io/address/0x432d810484add7454ddb3b5311f0ac2e95cecea8#writeContract) oder [Blockscout](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=write_contract) verwenden lassen, um mit Ihren Verträgen zu interagieren. Das wird für die erfahrenen Ethereans großartig sein. Aber wir versuchen, [eine weitere Milliarde Menschen](https://blog.ethereum.org/2021/05/07/ethereum-for-the-next-billion) zu erreichen. Dies wird ohne eine großartige Benutzererfahrung nicht geschehen, und eine benutzerfreundliche Oberfläche ist ein großer Teil davon. + +## Greeter-Anwendung {#greeter-app} + +Es gibt eine Menge Theorie dahinter, wie eine moderne Benutzeroberfläche funktioniert, und [viele gute Seiten](https://react.dev/learn/thinking-in-react), [die es erklären](https://wagmi.sh/core/getting-started). Anstatt die gute Arbeit dieser Seiten zu wiederholen, gehe ich davon aus, dass Sie es vorziehen, durch praktische Anwendung zu lernen und mit einer Anwendung zu beginnen, mit der Sie herumspielen können. Sie brauchen immer noch die Theorie, um Dinge zu erledigen, und wir werden dazu kommen – wir werden einfach Quelldatei für Quelldatei durchgehen und die Dinge besprechen, wenn wir zu ihnen kommen. + +### Installation {#installation} + +1. Fügen Sie bei Bedarf [die Holesky-Blockchain](https://chainlist.org/?search=holesky&testnets=true) zu Ihrer Wallet hinzu und [holen Sie sich Test-ETH](https://www.holeskyfaucet.io/). + +2. Klonen Sie das GitHub-Repository. + + ```sh + git clone https://github.com/qbzzt/20230801-modern-ui.git + ``` + +3. Installieren Sie die erforderlichen Pakete. + + ```sh + cd 20230801-modern-ui + pnpm install + ``` + +4. Starten Sie die Anwendung. + + ```sh + pnpm dev + ``` + +5. Navigieren Sie zu der von der Anwendung angezeigten URL. In den meisten Fällen ist dies [http://localhost:5173/](http://localhost:5173/). + +6. Sie können den Quellcode des Vertrags, eine leicht modifizierte Version von Hardhat's Greeter, [auf einem Blockchain-Explorer](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contract) sehen. + +### Dateidurchsicht {#file-walk-through} + +#### `index.html` {#index-html} + +Diese Datei ist ein Standard-HTML-Boilerplate mit Ausnahme dieser Zeile, die die Skriptdatei importiert. + +```html + +``` + +#### `src/main.tsx` {#main-tsx} + +Die Dateierweiterung verrät uns, dass es sich bei dieser Datei um eine [React-Komponente](https://www.w3schools.com/react/react_components.asp) handelt, die in [TypeScript](https://www.typescriptlang.org/) geschrieben wurde, einer Erweiterung von JavaScript, die eine [Typüberprüfung](https://en.wikipedia.org/wiki/Type_system#Type_checking) unterstützt. TypeScript wird in JavaScript kompiliert, sodass wir es für die clientseitige Ausführung verwenden können. + +```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' +``` + +Importieren Sie den benötigten Bibliotheks-Code. + +```tsx +import { App } from './App' +``` + +Importieren Sie die React-Komponente, die die Anwendung implementiert (siehe unten). + +```tsx +ReactDOM.createRoot(document.getElementById('root')!).render( +``` + +Erstellen Sie die Root-React-Komponente. Der Parameter für `render` ist [JSX](https://www.w3schools.com/react/react_jsx.asp), eine Erweiterungssprache, die sowohl HTML als auch JavaScript/TypeScript verwendet. Das Ausrufezeichen hier teilt dem TypeScript-Compiler mit: "Auch wenn nicht verifiziert werden kann, dass `document.getElementById('root')` ein gültiger Parameter für `ReactDOM.createRoot` sein wird, keine Sorge – ich als Entwickler garantiere, dass er vorhanden sein wird". + +```tsx + +``` + +Die Anwendung befindet sich in [einer `React.StrictMode`-Komponente](https://react.dev/reference/react/StrictMode). Diese Komponente weist die React-Bibliothek an, zusätzliche Debugging-Prüfungen einzufügen, was während der Entwicklung nützlich ist. + +```tsx + +``` + +Die Anwendung befindet sich auch in [einer `WagmiConfig`-Komponente](https://wagmi.sh/react/api/WagmiProvider). [Die wagmi (we are going to make it) Bibliothek](https://wagmi.sh/) verbindet die React-UI-Definitionen mit [der viem-Bibliothek](https://viem.sh/) zum Schreiben einer dezentralisierten Ethereum-Anwendung. + +```tsx + +``` + +Und schließlich [eine `RainbowKitProvider`-Komponente](https://www.rainbowkit.com/). Diese Komponente kümmert sich um die Anmeldung und die Kommunikation zwischen der Wallet und der Anwendung. + +```tsx + +``` + +Jetzt können wir die Komponente für die Anwendung haben, die die Benutzeroberfläche tatsächlich implementiert. Das `/>` am Ende der Komponente teilt React mit, dass diese Komponente gemäß dem XML-Standard keine Definitionen in sich enthält. + +```tsx + + + , +) +``` + +Natürlich müssen wir auch die anderen Komponenten schließen. + +#### `src/App.tsx` {#app-tsx} + +```tsx +import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useAccount } from 'wagmi' +import { Greeter } from './components/Greeter' + +export function App() { +``` + +Dies ist die Standardmethode zum Erstellen einer React-Komponente – definieren Sie eine Funktion, die jedes Mal aufgerufen wird, wenn sie gerendert werden muss. Diese Funktion hat typischerweise am Anfang etwas TypeScript- oder JavaScript-Code, gefolgt von einer `return`-Anweisung, die den JSX-Code zurückgibt. + +```tsx + const { isConnected } = useAccount() +``` + +Hier verwenden wir [`useAccount`](https://wagmi.sh/react/api/hooks/useAccount), um zu prüfen, ob wir über eine Wallet mit einer Blockchain verbunden sind oder nicht. + +Konventionsgemäß sind in React Funktionen, die `use...` heißen, [Hooks](https://www.w3schools.com/react/react_hooks.asp), die eine Art von Daten zurückgeben. Wenn Sie solche Hooks verwenden, erhält Ihre Komponente nicht nur die Daten, sondern wenn sich diese Daten ändern, wird die Komponente mit den aktualisierten Informationen neu gerendert. + +```tsx + return ( + <> +``` + +Das JSX einer React-Komponente _muss_ eine Komponente zurückgeben. Wenn wir mehrere Komponenten haben und nichts haben, was sie "natürlich" umschließt, verwenden wir eine leere Komponente (`<> ...` `), um sie zu einer einzigen Komponente zu machen. + +```tsx +

Greeter

+ +``` + +Wir erhalten [die `ConnectButton`-Komponente](https://www.rainbowkit.com/docs/connect-button) von RainbowKit. Wenn wir nicht verbunden sind, erhalten wir eine `Connect Wallet`-Schaltfläche, die ein modales Fenster öffnet, das Wallets erklärt und Sie auswählen lässt, welche Sie verwenden. Wenn wir verbunden sind, werden die von uns verwendete Blockchain, unsere Kontoadresse und unser ETH-Guthaben angezeigt. Wir können diese Anzeigen verwenden, um das Netzwerk zu wechseln oder die Verbindung zu trennen. + +```tsx + {isConnected && ( +``` + +Wenn wir tatsächliches JavaScript (oder TypeScript, das zu JavaScript kompiliert wird) in ein JSX einfügen müssen, verwenden wir Klammern (`{}`). + +Die Syntax `a && b` ist eine Kurzform für [`a ?` b : a`](https://www.w3schools.com/react/react_es6_ternary.asp). Das heißt, wenn `a`wahr ist, wird es zu`b`ausgewertet, andernfalls zu`a`(was`false`, `0` usw. sein kann). Dies ist eine einfache Möglichkeit, React mitzuteilen, dass eine Komponente nur dann angezeigt werden soll, wenn eine bestimmte Bedingung erfüllt ist. + +In diesem Fall wollen wir dem Benutzer `Greeter` nur dann anzeigen, wenn der Benutzer mit einer Blockchain verbunden ist. + +```tsx + + )} + + ) +} +``` + +#### `src/components/Greeter.tsx` {#greeter-tsx} + +Diese Datei enthält den größten Teil der UI-Funktionalität. Es enthält Definitionen, die normalerweise in mehreren Dateien wären, aber da dies ein Tutorial ist, ist das Programm so optimiert, dass es beim ersten Mal leicht zu verstehen ist, anstatt auf Leistung oder einfache Wartung. + +```tsx +import { useState, ChangeEventHandler } from 'react' +import { useNetwork, + useReadContract, + usePrepareContractWrite, + useContractWrite, + useContractEvent + } from 'wagmi' +``` + +Wir verwenden diese Bibliotheksfunktionen. Auch hier werden sie unten erklärt, wo sie verwendet werden. + +```tsx +import { AddressType } from 'abitype' +``` + +[Die `abitype`-Bibliothek](https://abitype.dev/) stellt uns TypeScript-Definitionen für verschiedene Ethereum-Datentypen zur Verfügung, wie z. B. [`AddressType`](https://abitype.dev/config#addresstype). + +```tsx +let greeterABI = [ + . + . + . +] as const // greeterABI +``` + +Das ABI für den `Greeter`-Vertrag. +Wenn Sie die Verträge und die Benutzeroberfläche gleichzeitig entwickeln, würden Sie sie normalerweise im selben Repository ablegen und das vom Solidity-Compiler generierte ABI als Datei in Ihrer Anwendung verwenden. Dies ist hier jedoch nicht notwendig, da der Vertrag bereits entwickelt ist und sich nicht ändern wird. + +```tsx +type AddressPerBlockchainType = { + [key: number]: AddressType +} +``` + +TypeScript ist stark typisiert. Wir verwenden diese Definition, um die Adresse anzugeben, in der der `Greeter`-Vertrag auf verschiedenen Ketten bereitgestellt wird. Der Schlüssel ist eine Zahl (die Chain-ID) und der Wert ist ein `AddressType` (eine Adresse). + +```tsx +const contractAddrs: AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' +} +``` + +Die Adresse des Vertrags in den beiden unterstützten Netzwerken: [Holesky](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contact_code) und [Sepolia](https://eth-sepolia.blockscout.com/address/0x7143d5c190F048C8d19fe325b748b081903E3BF0?tab=contact_code). + +Hinweis: Es gibt tatsächlich eine dritte Definition für Redstone Holesky, die weiter unten erläutert wird. + +```tsx +type ShowObjectAttrsType = { + name: string, + object: any +} +``` + +Dieser Typ wird als Parameter für die `ShowObject`-Komponente verwendet (wird später erklärt). Er enthält den Namen des Objekts und seinen Wert, die zu Debugging-Zwecken angezeigt werden. + +```tsx +type ShowGreetingAttrsType = { + greeting: string | undefined +} +``` + +Zu jedem Zeitpunkt können wir entweder wissen, was die Begrüßung ist (weil wir sie von der Blockchain gelesen haben) oder nicht wissen (weil wir sie noch nicht erhalten haben). Es ist also nützlich, einen Typ zu haben, der entweder ein String oder nichts sein kann. + +##### `Greeter`-Komponente {#greeter-component} + +```tsx +const Greeter = () => { +``` + +Schließlich definieren wir die Komponente. + +```tsx + const { chain } = useNetwork() +``` + +Informationen über die von uns verwendete Chain, bereitgestellt von [wagmi](https://wagmi.sh/react/hooks/useNetwork). +Da es sich um einen Hook (`use...`) handelt, wird die Komponente bei jeder Änderung dieser Information neu gezeichnet. + +```tsx + const greeterAddr = chain && contractAddrs[chain.id] +``` + +Die Adresse des Greeter-Vertrags, die je nach Chain variiert (und `undefined` ist, wenn wir keine Chain-Informationen haben oder uns auf einer Chain ohne diesen Vertrag befinden). + +```tsx + const readResults = useReadContract({ + address: greeterAddr, + abi: greeterABI, + functionName: "greet" , // Keine Argumente + watch: true + }) +``` + +[Der `useReadContract`-Hook](https://wagmi.sh/react/api/hooks/useReadContract) liest Informationen aus einem Vertrag. Sie können genau sehen, welche Informationen es zurückgibt, wenn Sie `readResults` in der Benutzeroberfläche erweitern. In diesem Fall möchten wir, dass es weiter sucht, damit wir informiert werden, wenn sich die Begrüßung ändert. + +**Hinweis:** Wir könnten auf [`setGreeting`-Ereignisse](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=logs) lauschen, um zu wissen, wann sich die Begrüßung ändert, und auf diese Weise aktualisieren. Obwohl dies effizienter sein mag, ist es nicht in allen Fällen anwendbar. Wenn der Benutzer zu einer anderen Kette wechselt, ändert sich auch die Begrüßung, aber diese Änderung wird nicht von einem Ereignis begleitet. Wir könnten einen Teil des Codes haben, der auf Ereignisse lauscht, und einen anderen, um Kettenänderungen zu identifizieren, aber das wäre komplizierter, als nur [den `watch`-Parameter](https://wagmi.sh/react/api/hooks/useReadContract#watch-optional) zu setzen. + +```tsx + const [ newGreeting, setNewGreeting ] = useState("") +``` + +Der [`useState`-Hook](https://www.w3schools.com/react/react_usestate.asp) von React ermöglicht es uns, eine Zustandsvariable anzugeben, deren Wert von einem Rendering der Komponente zum nächsten erhalten bleibt. Der Anfangswert ist der Parameter, in diesem Fall der leere String. + +Der `useState`-Hook gibt eine Liste mit zwei Werten zurück: + +1. Der aktuelle Wert der Zustandsvariable. +2. Eine Funktion, um die Zustandsvariable bei Bedarf zu ändern. Da dies ein Hook ist, wird die Komponente jedes Mal neu gerendert, wenn sie aufgerufen wird. + +In diesem Fall verwenden wir eine Zustandsvariable für die neue Begrüßung, die der Benutzer setzen möchte. + +```tsx + const greetingChange : ChangeEventHandler = (evt) => + setNewGreeting(evt.target.value) +``` + +Dies ist der Ereignis-Handler für den Fall, dass sich das Eingabefeld für die neue Begrüßung ändert. Der Typ [`ChangeEventHandler`](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/) gibt an, dass dies ein Handler für eine Wertänderung eines HTML-Eingabeelements ist. Der Teil `` wird verwendet, da es sich um einen [generischen Typ](https://www.w3schools.com/typescript/typescript_basic_generics.php) handelt. + +```tsx + const preparedTx = usePrepareContractWrite({ + address: greeterAddr, + abi: greeterABI, + functionName: 'setGreeting', + args: [ newGreeting ] + }) + const workingTx = useContractWrite(preparedTx.config) +``` + +Dies ist der Prozess, um eine Blockchain-Transaktion aus der Client-Perspektive zu übermitteln: + +1. Senden Sie die Transaktion an einen Knoten in der Blockchain unter Verwendung von [`eth_estimateGas`](https://docs.alchemy.com/reference/eth-estimategas). +2. Warten Sie auf eine Antwort vom Knoten. +3. Wenn die Antwort eingegangen ist, bitten Sie den Benutzer, die Transaktion über die Wallet zu signieren. Dieser Schritt _muss_ erfolgen, nachdem die Antwort des Knotens eingegangen ist, da dem Benutzer die Gaskosten der Transaktion vor dem Signieren angezeigt werden. +4. Warten Sie auf die Genehmigung durch den Benutzer. +5. Senden Sie die Transaktion erneut, diesmal mit [`eth_sendRawTransaction`](https://docs.alchemy.com/reference/eth-sendrawtransaction). + +Schritt 2 wird wahrscheinlich eine spürbare Zeit in Anspruch nehmen, während der sich die Benutzer fragen würden, ob ihr Befehl wirklich von der Benutzeroberfläche empfangen wurde und warum sie nicht bereits gebeten werden, die Transaktion zu signieren. Das sorgt für eine schlechte Benutzererfahrung (UX). + +Die Lösung besteht darin, [Prepare-Hooks](https://wagmi.sh/react/prepare-hooks) zu verwenden. Jedes Mal, wenn sich ein Parameter ändert, senden Sie sofort die `eth_estimateGas`-Anfrage an den Knoten. Wenn der Benutzer dann tatsächlich die Transaktion senden möchte (in diesem Fall durch Drücken von **Gruß aktualisieren**), sind die Gaskosten bekannt und der Benutzer kann die Wallet-Seite sofort sehen. + +```tsx + return ( +``` + +Jetzt können wir endlich das eigentliche HTML zur Rückgabe erstellen. + +```tsx + <> +

Greeter

+ { + !readResults.isError && !readResults.isLoading && + + } +
+``` + +Erstellen Sie eine `ShowGreeting`-Komponente (wird unten erklärt), aber nur, wenn die Begrüßung erfolgreich von der Blockchain gelesen wurde. + +```tsx + +``` + +Dies ist das Eingabefeld, in dem der Benutzer eine neue Begrüßung festlegen kann. Jedes Mal, wenn der Benutzer eine Taste drückt, rufen wir `greetingChange` auf, was `setNewGreeting` aufruft. Da `setNewGreeting` aus dem `useState`-Hook stammt, wird die `Greeter`-Komponente erneut gerendert. Dies bedeutet, dass: + +- Wir müssen `value` angeben, um den Wert der neuen Begrüßung beizubehalten, da er sich sonst wieder in den Standardwert, den leeren String, zurückverwandeln würde. +- `usePrepareContractWrite` wird jedes Mal aufgerufen, wenn sich `newGreeting` ändert, was bedeutet, dass es immer das neueste `newGreeting` in der vorbereiteten Transaktion haben wird. + +```tsx + +``` + +Wenn kein `workingTx.write` vorhanden ist, warten wir noch auf Informationen, die zum Senden der Grußaktualisierung erforderlich sind, sodass die Schaltfläche deaktiviert ist. Wenn ein `workingTx.write`-Wert vorhanden ist, ist dies die Funktion, die aufgerufen werden muss, um die Transaktion zu senden. + +```tsx +
+ + + + + ) +} +``` + +Schließlich, um Ihnen zu helfen zu sehen, was wir tun, zeigen wir die drei Objekte, die wir verwenden: + +- `readResults` +- `preparedTx` +- `workingTx` + +##### `ShowGreeting`-Komponente {#showgreeting-component} + +Diese Komponente zeigt + +```tsx +const ShowGreeting = (attrs : ShowGreetingAttrsType) => { +``` + +Eine Komponentenfunktion erhält einen Parameter mit allen Attributen der Komponente. + +```tsx + return {attrs.greeting} +} +``` + +##### `ShowObject`-Komponente {#showobject-component} + +Zu Informationszwecken verwenden wir die `ShowObject`-Komponente, um die wichtigen Objekte anzuzeigen (`readResults` zum Lesen der Begrüßung und `preparedTx` und `workingTx` für von uns erstellte Transaktionen). + +```tsx +const ShowObject = (attrs: ShowObjectAttrsType ) => { + const keys = Object.keys(attrs.object) + const funs = keys.filter(k => typeof attrs.object[k] == "function") + return <> +
+``` + +Wir möchten die Benutzeroberfläche nicht mit allen Informationen überladen. Um sie also ansehen oder schließen zu können, verwenden wir ein [`details`](https://www.w3schools.com/tags/tag_details.asp)-Tag. + +```tsx + {attrs.name} +
+        {JSON.stringify(attrs.object, null, 2)}
+```
+
+Die meisten Felder werden mit [`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp) angezeigt.
+
+```tsx
+      
+ { funs.length > 0 && + <> + Funktionen: +
    +``` + +Die Ausnahme sind Funktionen, die nicht Teil des [JSON-Standards](https://www.json.org/json-en.html) sind und daher separat angezeigt werden müssen. + +```tsx + {funs.map((f, i) => +``` + +Innerhalb von JSX wird der Code in `{` geschweiften Klammern `}` als JavaScript interpretiert. Dann wird der Code innerhalb der `(` runden Klammern `)` wieder als JSX interpretiert. + +```tsx + (
  • {f}
  • ) + )} +``` + +React erfordert, dass Tags im [DOM-Baum](https://www.w3schools.com/js/js_htmldom.asp) eindeutige Bezeichner haben. Dies bedeutet, dass untergeordnete Elemente desselben Tags (in diesem Fall [die ungeordnete Liste](https://www.w3schools.com/tags/tag_ul.asp)) unterschiedliche `key`-Attribute benötigen. + +```tsx +
+ + } +
+ +} +``` + +Beenden Sie die verschiedenen HTML-Tags. + +##### Der abschließende `Export` {#the-final-export} + +```tsx +export { Greeter } +``` + +Die `Greeter`-Komponente ist diejenige, die wir für die Anwendung exportieren müssen. + +#### `src/wagmi.ts` {#wagmi-ts} + +Schließlich befinden sich verschiedene Definitionen in Bezug auf WAGMI in `src/wagmi.ts`. Ich werde hier nicht alles erklären, da das meiste davon Boilerplate ist, den Sie wahrscheinlich nicht ändern müssen. + +Der Code hier ist nicht genau derselbe wie [auf Github](https://github.com/qbzzt/20230801-modern-ui/blob/main/src/wagmi.ts), da wir später im Artikel eine weitere Chain ([Redstone Holesky](https://redstone.xyz/docs/network-info)) hinzufügen. + +```ts +import { getDefaultWallets } from '@rainbow-me/rainbowkit' +import { configureChains, createConfig } from 'wagmi' +import { holesky, sepolia } from 'wagmi/chains' +``` + +Importieren Sie die Blockchains, die die Anwendung unterstützt. Sie können die Liste der unterstützten Chains [im Viem-Github](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions) sehen. + +```ts +import { publicProvider } from 'wagmi/providers/public' + +const walletConnectProjectId = 'c96e690bb92b6311e8e9b2a6a22df575' +``` + +Um [WalletConnect](https://walletconnect.com/) verwenden zu können, benötigen Sie eine Projekt-ID für Ihre Anwendung. Sie können sie [auf cloud.walletconnect.com](https://cloud.walletconnect.com/sign-in) erhalten. + +```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 } +``` + +### Eine weitere Blockchain hinzufügen {#add-blockchain} + +Heutzutage gibt es eine Menge [L2-Skalierungslösungen](/layer-2/), und vielleicht möchten Sie einige unterstützen, die Viem noch nicht unterstützt. Dazu ändern Sie `src/wagmi.ts`. Diese Anweisungen erklären, wie man [Redstone Holesky](https://redstone.xyz/docs/network-info) hinzufügt. + +1. Importieren Sie den `defineChain`-Typ aus Viem. + + ```ts + import { defineChain } from 'viem' + ``` + +2. Fügen Sie die Netzwerkdefinition hinzu. + + ```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. Fügen Sie die neue Chain zum `configureChains`-Aufruf hinzu. + + ```ts + const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia, redstoneHolesky ], + [ publicProvider(), ], + ) + ``` + +4. Stellen Sie sicher, dass die Anwendung die Adresse für Ihre Verträge im neuen Netzwerk kennt. In diesem Fall ändern wir `src/components/Greeter.tsx`: + + ```ts + const contractAddrs : AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Redstone Holesky + 17001: '0x4919517f82a1B89a32392E1BF72ec827ba9986D3', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' + } + ``` + +## Fazit {#conclusion} + +Natürlich ist es Ihnen egal, ob Sie eine Benutzeroberfläche für `Greeter` bereitstellen. Sie möchten eine Benutzeroberfläche für Ihre eigenen Verträge erstellen. Um Ihre eigene Anwendung zu erstellen, führen Sie diese Schritte aus: + +1. Geben Sie an, dass eine Wagmi-Anwendung erstellt werden soll. + + ```sh copy + pnpm create wagmi + ``` + +2. Benennen Sie die Anwendung. + +3. Wählen Sie das **React**-Framework. + +4. Wählen Sie die **Vite**-Variante. + +5. Sie können [Rainbow Kit hinzufügen](https://www.rainbowkit.com/docs/installation#manual-setup). + +Jetzt können Sie loslegen und Ihre Verträge für die ganze Welt nutzbar machen. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). + diff --git a/public/content/translations/de/developers/tutorials/deploying-your-first-smart-contract/index.md b/public/content/translations/de/developers/tutorials/deploying-your-first-smart-contract/index.md new file mode 100644 index 00000000000..d9b688d7c9d --- /dev/null +++ b/public/content/translations/de/developers/tutorials/deploying-your-first-smart-contract/index.md @@ -0,0 +1,101 @@ +--- +title: Deinen ersten Smart Contract bereitstellen +description: "Eine Einführung in die Bereitstellung deines ersten Smart Contracts in einem Ethereum-Testnetzwerk" +author: "jdourlens" +tags: + [ + "intelligente Verträge", + "remix", + "solidity", + "Bereitstellung" + ] +skill: beginner +lang: de +published: 2020-04-03 +source: EthereumDev +sourceUrl: https://ethereumdev.io/deploying-your-first-smart-contract/ +address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +--- + +Ich nehme an, du bist genauso aufgeregt wie wir, deinen ersten [Smart Contract](/developers/docs/smart-contracts/) auf der Ethereum-Blockchain [bereitzustellen](/developers/docs/smart-contracts/deploying/) und mit ihm zu interagieren. + +Keine Sorge, da dies unser erster Smart Contract ist, werden wir ihn in einem [lokalen Testnetzwerk](/developers/docs/networks/) bereitstellen, sodass es dich nichts kostet, ihn bereitzustellen und so viel damit zu spielen, wie du möchtest. + +## Schreiben unseres Vertrags {#writing-our-contract} + +Der erste Schritt ist, [Remix zu besuchen](https://remix.ethereum.org/) und eine neue Datei zu erstellen. Füge im oberen linken Teil der Remix-Benutzeroberfläche eine neue Datei hinzu und gib den gewünschten Dateinamen ein. + +![Hinzufügen einer neuen Datei in der Remix-Benutzeroberfläche](./remix.png) + +In die neue Datei fügen wir den folgenden Code ein. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.17; + +contract Counter { + + // Öffentliche Variable vom Typ vorzeichenlose Ganzzahl, um die Anzahl der Zählvorgänge zu speichern + uint256 public count = 0; + + // Funktion, die unseren Zähler erhöht + function increment() public { + count += 1; + } + + // Nicht notwendiger Getter, um den Zählwert zu erhalten + function getCount() public view returns (uint256) { + return count; + } + +} +``` + +Wenn du mit dem Programmieren vertraut bist, kannst du leicht erraten, was dieses Programm tut. Hier ist eine Erklärung, Zeile für Zeile: + +- Zeile 4: Wir definieren einen Vertrag mit dem Namen `Counter`. +- Zeile 7: Unser Vertrag speichert eine vorzeichenlose Ganzzahl namens `count`, die bei 0 beginnt. +- Zeile 10: Die erste Funktion ändert den Zustand des Vertrags und erhöht (`increment()`) unsere Variable `count`. +- Zeile 15: Die zweite Funktion ist nur ein Getter, um den Wert der Variable `count` außerhalb des Smart Contracts auslesen zu können. Beachte, dass dies nicht notwendig ist, da wir unsere `count`-Variable als öffentlich (public) definiert haben, sondern nur als Beispiel dient. + +Das ist alles für unseren ersten einfachen Smart Contract. Wie du vielleicht weißt, sieht es aus wie eine Klasse aus OOP-Sprachen (objektorientierte Programmierung) wie Java oder C++. Jetzt ist es an der Zeit, mit unserem Vertrag zu spielen. + +## Bereitstellung unseres Vertrags {#deploying-our-contract} + +Nachdem wir unseren allerersten Smart Contract geschrieben haben, werden wir ihn nun in der Blockchain bereitstellen, um damit spielen zu können. + +[Die Bereitstellung des Smart Contracts in der Blockchain](/developers/docs/smart-contracts/deploying/) ist eigentlich nur das Senden einer Transaktion, die den Code des kompilierten Smart Contracts enthält, ohne einen Empfänger anzugeben. + +Zuerst [kompilieren wir den Vertrag](/developers/docs/smart-contracts/compiling/), indem wir auf das Kompilieren-Symbol auf der linken Seite klicken: + +![Das Kompilieren-Symbol in der Remix-Symbolleiste](./remix-compile-button.png) + +Klicke dann auf die Schaltfläche „Kompilieren“: + +![Die Schaltfläche „Kompilieren“ im Remix-Solidity-Compiler](./remix-compile.png) + +Du kannst die Option „Auto compile“ wählen, damit der Vertrag immer kompiliert wird, wenn du den Inhalt im Texteditor speicherst. + +Navigiere dann zum Bildschirm „Deploy and run transactions“: + +![Das Bereitstellungssymbol in der Remix-Symbolleiste](./remix-deploy.png) + +Sobald du auf dem Bildschirm „Deploy and run transactions“ bist, überprüfe, ob dein Vertragsname erscheint, und klicke auf „Deploy“. Wie du oben auf der Seite sehen kannst, ist die aktuelle Umgebung „JavaScript VM“. Das bedeutet, dass wir unseren Smart Contract in einer lokalen Test-Blockchain bereitstellen und mit ihm interagieren, um schneller und ohne Gebühren testen zu können. + +![Die Schaltfläche „Deploy“ im Remix-Solidity-Compiler](./remix-deploy-button.png) + +Sobald du auf die Schaltfläche „Deploy“ geklickt hast, siehst du deinen Vertrag unten erscheinen. Klicke auf den Pfeil links, um ihn zu erweitern, damit wir den Inhalt unseres Vertrags sehen. Das sind unsere Variable `counter`, unsere Funktion `increment()` und der Getter `getCounter()`. + +Wenn du auf die Schaltfläche `count` oder `getCount` klickst, wird der Inhalt der `count`-Variable des Vertrags abgerufen und angezeigt. Da wir die Funktion `increment` noch nicht aufgerufen haben, sollte 0 angezeigt werden. + +![Die Funktionsschaltfläche im Remix-Solidity-Compiler](./remix-function-button.png) + +Rufen wir nun die Funktion `increment` auf, indem wir auf die Schaltfläche klicken. Du wirst am unteren Rand des Fensters die Protokolle der durchgeführten Transaktionen sehen. Du wirst sehen, dass die Protokolle anders aussehen, wenn du die Schaltfläche zum Abrufen der Daten anstelle der Schaltfläche `increment` drückst. Das liegt daran, dass das Lesen von Daten auf der Blockchain keine Transaktionen (Schreibvorgänge) oder Gebühren erfordert. Denn nur die Änderung des Zustands der Blockchain erfordert eine Transaktion: + +![Ein Transaktionsprotokoll](./transaction-log.png) + +Nachdem du die Schaltfläche `increment` gedrückt hast, die eine Transaktion zum Aufruf unserer `increment()`-Funktion generiert, lesen wir den neu aktualisierten Zustand unseres Smart Contracts, wenn wir wieder auf die Schaltflächen `count` oder `getCount` klicken, wobei die Variable `count` größer als 0 ist. + +![Neu aktualisierter Zustand des Smart Contracts](./updated-state.png) + +Im nächsten Tutorial befassen wir uns damit, [wie du Events zu deinen Smart Contracts hinzufügen kannst](/developers/tutorials/logging-events-smart-contracts/). Die Protokollierung von Events ist eine bequeme Möglichkeit, deinen Smart Contract zu debuggen und zu verstehen, was beim Aufruf einer Funktion passiert. diff --git a/public/content/translations/de/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md b/public/content/translations/de/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md new file mode 100644 index 00000000000..889b358a2fa --- /dev/null +++ b/public/content/translations/de/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md @@ -0,0 +1,372 @@ +--- +title: Wie man eine Dapp auf einem lokalen Multi-Client-Testnet entwickelt und testet +description: "Dieser Leitfaden führt Sie zunächst durch die Instanziierung und Konfiguration eines lokalen Multi-Client-Ethereum-Testnets, bevor Sie das Testnet zur Bereitstellung und zum Testen einer Dapp verwenden." +author: "Tedi Mitiku" +tags: + [ + "Clients", + "Nodes", + "intelligente Verträge", + "Komponierbarkeit", + "Konsensebene", + "Ausführungsebene", + "testen" + ] +skill: intermediate +lang: de +published: 2023-04-11 +--- + +## Einführung {#introduction} + +Dieser Leitfaden führt Sie durch den Prozess der Instanziierung eines konfigurierbaren lokalen Ethereum-Testnets, der Bereitstellung eines Smart Contracts und der Verwendung des Testnets, um Tests mit Ihrer Dapp durchzuführen. Dieser Leitfaden richtet sich an Dapp-Entwickler, die ihre Dapps lokal mit verschiedenen Netzwerkkonfigurationen entwickeln und testen möchten, bevor sie sie auf einem Live-Testnet oder dem Mainnet bereitstellen. + +In diesem Leitfaden werden Sie: + +- Ein lokales Ethereum-Testnet mit dem [`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package) unter Verwendung von [Kurtosis](https://www.kurtosis.com/) instanziieren, +- Ihre Hardhat Dapp-Entwicklungsumgebung mit dem lokalen Testnet verbinden, um eine Dapp zu kompilieren, bereitzustellen und zu testen, und +- Das lokale Testnet konfigurieren, einschließlich Parametern wie der Anzahl der Nodes und spezifischen EL/CL-Client-Paarungen, um Entwicklungs- und Test-Workflows für verschiedene Netzwerkkonfigurationen zu ermöglichen. + +### Was ist Kurtosis? {#what-is-kurtosis} + +[Kurtosis](https://www.kurtosis.com/) ist ein zusammensetzbares Build-System, das für die Konfiguration von Multi-Container-Testumgebungen entwickelt wurde. Es ermöglicht Entwicklern insbesondere, reproduzierbare Umgebungen zu erstellen, die eine dynamische Einrichtungslogik erfordern, wie zum Beispiel Blockchain-Testnets. + +In diesem Leitfaden startet das Kurtosis-eth-network-package ein lokales Ethereum-Testnet mit Unterstützung für den [`geth`](https://geth.ethereum.org/) Ausführungsebenen-Client (EL) sowie die [`teku`](https://consensys.io/teku), [`lighthouse`](https://lighthouse.sigmaprime.io/) und [`lodestar`](https://lodestar.chainsafe.io/) Konsensebenen-Clients (CL). Dieses Paket dient als eine konfigurierbare und zusammensetzbare Alternative zu Netzwerken in Frameworks wie Hardhat Network, Ganache und Anvil. Kurtosis bietet Entwicklern mehr Kontrolle und Flexibilität über die von ihnen verwendeten Testnets, was ein Hauptgrund dafür ist, dass die [Ethereum Foundation Kurtosis zum Testen des Merge verwendet hat](https://www.kurtosis.com/blog/testing-the-ethereum-merge) und es weiterhin zum Testen von Netzwerk-Upgrades verwendet. + +## Einrichten von Kurtosis {#setting-up-kurtosis} + +Bevor Sie fortfahren, stellen Sie sicher, dass Sie Folgendes haben: + +- [Die Docker-Engine auf Ihrem lokalen Rechner installiert und gestartet](https://docs.kurtosis.com/install/#i-install--start-docker) +- [Die Kurtosis CLI installiert](https://docs.kurtosis.com/install#ii-install-the-cli) (oder auf die neueste Version aktualisiert, falls Sie die CLI bereits installiert haben) +- [Node.js](https://nodejs.org/en), [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) und [npx](https://www.npmjs.com/package/npx) installiert (für Ihre Dapp-Umgebung) + +## Ein lokales Ethereum-Testnet instanziieren {#instantiate-testnet} + +Um ein lokales Ethereum-Testnet zu starten, führen Sie Folgendes aus: + +```python +kurtosis --enclave local-eth-testnet run github.com/kurtosis-tech/eth-network-package +``` + +Hinweis: Dieser Befehl benennt Ihr Netzwerk: \"local-eth-testnet\" über das `--enclave`-Flag. + +Kurtosis gibt die Schritte aus, die im Hintergrund durchgeführt werden, während es die Anweisungen interpretiert, validiert und ausführt. Am Ende sollten Sie eine Ausgabe sehen, die der folgenden ähnelt: + +```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 + +``` + +Herzlichen Glückwunsch! Sie haben Kurtosis verwendet, um ein lokales Ethereum-Testnet mit einem CL- (`lighthouse`) und einem EL-Client (`geth`) über Docker zu instanziieren. + +### Überprüfung {#review-instantiate-testnet} + +In diesem Abschnitt haben Sie einen Befehl ausgeführt, der Kurtosis anwies, das [remote auf GitHub gehostete `eth-network-package`](https://github.com/kurtosis-tech/eth-network-package) zu verwenden, um ein lokales Ethereum-Testnet innerhalb einer Kurtosis [Enclave](https://docs.kurtosis.com/advanced-concepts/enclaves/) zu starten. Innerhalb Ihrer Enklave finden Sie sowohl \"Datei-Artefakte\" als auch \"Benutzerdienste\". + +Die [Datei-Artefakte](https://docs.kurtosis.com/advanced-concepts/files-artifacts/) in Ihrer Enklave enthalten alle Daten, die generiert und verwendet werden, um die EL- und CL-Clients zu booten. Die Daten wurden mit dem `prelaunch-data-generator`-Dienst erstellt, der aus diesem [Docker-Image](https://github.com/ethpandaops/ethereum-genesis-generator) gebaut wurde + +Benutzerdienste zeigen alle containerisierten Dienste an, die in Ihrer Enklave betrieben werden. Sie werden feststellen, dass ein einzelner Node erstellt wurde, der sowohl einen EL-Client als auch einen CL-Client enthält. + +## Verbinden Sie Ihre Dapp-Entwicklungsumgebung mit dem lokalen Ethereum-Testnet {#connect-your-dapp} + +### Einrichten der Dapp-Entwicklungsumgebung {#set-up-dapp-env} + +Nachdem Sie nun ein laufendes lokales Testnet haben, können Sie Ihre Dapp-Entwicklungsumgebung mit Ihrem lokalen Testnet verbinden. In diesem Leitfaden wird das Hardhat-Framework verwendet, um eine Blackjack-Dapp auf Ihrem lokalen Testnet bereitzustellen. + +Um Ihre Dapp-Entwicklungsumgebung einzurichten, klonen Sie das Repository, das unsere Beispiel-Dapp enthält, und installieren Sie dessen Abhängigkeiten. Führen Sie dazu Folgendes aus: + +```python +git clone https://github.com/kurtosis-tech/awesome-kurtosis.git && cd awesome-kurtosis/smart-contract-example && yarn +``` + +Der hier verwendete Ordner [smart-contract-example](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example) enthält das typische Setup für einen Dapp-Entwickler, der das [Hardhat](https://hardhat.org/)-Framework verwendet: + +- [`contracts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/contracts) enthält einige einfache Smart Contracts für eine Blackjack-Dapp +- [`scripts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/scripts) enthält ein Skript zur Bereitstellung eines Token-Vertrags in Ihrem lokalen Ethereum-Netzwerk +- [`test/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/test) enthält einen einfachen .js-Test für Ihren Token-Vertrag, um zu bestätigen, dass für jeden Spieler in unserer Blackjack-Dapp 1000 gemintet wurden +- [`hardhat.config.ts`](https://github.com/kurtosis-tech/awesome-kurtosis/blob/main/smart-contract-example/hardhat.config.ts) konfiguriert Ihr Hardhat-Setup + +### Hardhat für die Verwendung des lokalen Testnets konfigurieren {#configure-hardhat} + +Nachdem Ihre Dapp-Entwicklungsumgebung eingerichtet ist, verbinden Sie nun Hardhat, um das mit Kurtosis generierte lokale Ethereum-Testnet zu verwenden. Um dies zu erreichen, ersetzen Sie `<$YOUR_PORT>` in der `localnet`-Struktur in Ihrer `hardhat.config.ts`-Konfigurationsdatei mit dem Port der RPC-URI-Ausgabe eines beliebigen `el-client-`-Dienstes. In diesem Beispielfall wäre der Port `64248`. Ihr Port wird ein anderer sein. + +Beispiel in `hardhat.config.ts`: + +```js +localnet: { +url: 'http://127.0.0.1:<$YOUR_PORT>',// TODO: ERSETZEN SIE $YOUR_PORT DURCH DEN PORT EINER NODE-URI, DIE VOM ETH-NETZWERK-KURTOSIS-PAKET ERZEUGT WURDE + +// Dies sind private Schlüssel, die mit vorfinanzierten Testkonten verbunden sind, die vom eth-network-package erstellt wurden +// +accounts: [ + "ef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2", + "48fcc39ae27a0e8bf0274021ae6ebd8fe4a0e12623d61464c498900b28feb567", + "7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31", + "b3c409b6b0b3aa5e65ab2dc1930534608239a478106acf6f3d9178e9f9b00b35", + "df9bb6de5d3dc59595bcaa676397d837ff49441d211878c024eabda2cd067c9f", + "7da08f856b5956d40a72968f93396f6acff17193f013e8053f6fbb6c08c194d6", + ], +}, +``` + +Sobald Sie Ihre Datei gespeichert haben, ist Ihre Hardhat Dapp-Entwicklungsumgebung mit Ihrem lokalen Ethereum-Testnet verbunden! Sie können überprüfen, ob Ihr Testnet funktioniert, indem Sie Folgendes ausführen: + +```python +npx hardhat balances --network localnet +``` + +Die Ausgabe sollte etwa so aussehen: + +```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 +``` + +Dies bestätigt, dass Hardhat Ihr lokales Testnet verwendet und die vom `eth-network-package` erstellten vorfinanzierten Konten erkennt. + +### Ihre Dapp lokal bereitstellen und testen {#deploy-and-test-dapp} + +Nachdem die Dapp-Entwicklungsumgebung vollständig mit dem lokalen Ethereum-Testnet verbunden ist, können Sie nun Entwicklungs- und Test-Workflows für Ihre Dapp mit dem lokalen Testnet ausführen. + +Um den `ChipToken.sol`-Smart-Contract für lokales Prototyping und Entwicklung zu kompilieren und bereitzustellen, führen Sie Folgendes aus: + +```python +npx hardhat compile +npx hardhat run scripts/deploy.ts --network localnet +``` + +Die Ausgabe sollte etwa so aussehen: + +```python +ChipToken deployed to: 0xAb2A01BC351770D09611Ac80f1DE076D56E0487d +``` + +Führen Sie nun den `simple.js`-Test für Ihre lokale Dapp aus, um zu bestätigen, dass für jeden Spieler in unserer Blackjack-Dapp 1000 gemintet wurden: + +Die Ausgabe sollte etwa so aussehen: + +```python +npx hardhat test --network localnet +``` + +Die Ausgabe sollte etwa so aussehen: + +```python +ChipToken + mint + ✔ sollte 1000 Chips für SPIELER EINS minten + + 1 bestanden (654ms) +``` + +### Überprüfung {#review-dapp-workflows} + +An diesem Punkt haben Sie nun eine Dapp-Entwicklungsumgebung eingerichtet, sie mit einem von Kurtosis erstellten lokalen Ethereum-Netzwerk verbunden und einen einfachen Test für Ihre Dapp kompiliert, bereitgestellt und ausgeführt. + +Lassen Sie uns nun untersuchen, wie Sie das zugrunde liegende Netzwerk für das Testen unserer Dapps unter verschiedenen Netzwerkkonfigurationen konfigurieren können. + +## Konfigurieren des lokalen Ethereum-Testnets {#configure-testnet} + +### Ändern der Client-Konfigurationen und der Anzahl der Nodes {#configure-client-config-and-num-nodes} + +Ihr lokales Ethereum-Testnet kann so konfiguriert werden, dass es verschiedene EL- und CL-Client-Paare sowie eine unterschiedliche Anzahl von Nodes verwendet, je nach Szenario und spezifischer Netzwerkkonfiguration, die Sie entwickeln oder testen möchten. Das bedeutet, dass Sie nach der Einrichtung ein angepasstes lokales Testnet starten und damit dieselben Arbeitsabläufe (Bereitstellung, Tests usw.) ausführen können. unter verschiedenen Netzwerkkonfigurationen, um sicherzustellen, dass alles wie erwartet funktioniert. Um mehr über die anderen Parameter zu erfahren, die Sie ändern können, besuchen Sie diesen Link. + +Probieren Sie es aus! Sie können verschiedene Konfigurationsoptionen über eine JSON-Datei an das `eth-network-package` übergeben. Diese Netzwerkparameter-JSON-Datei stellt die spezifischen Konfigurationen bereit, die Kurtosis zum Einrichten des lokalen Ethereum-Netzwerks verwenden wird. + +Nehmen Sie die Standardkonfigurationsdatei und bearbeiten Sie sie, um drei Nodes mit unterschiedlichen EL/CL-Paaren zu starten: + +- Node 1 mit `geth`/`lighthouse` +- Node 2 mit `geth`/`lodestar` +- Node 3 mit `geth`/`teku` + +Diese Konfiguration erstellt ein heterogenes Netzwerk von Ethereum-Node-Implementierungen zum Testen Ihrer Dapp. Ihre Konfigurationsdatei sollte jetzt so aussehen: + +```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, + }, +} +``` + +Jede `participants`-Struktur entspricht einem Node im Netzwerk, sodass 3 `participants`-Strukturen Kurtosis anweisen, 3 Nodes in Ihrem Netzwerk zu starten. Jede `participants`-Struktur ermöglicht es Ihnen, das für diesen spezifischen Node verwendete EL- und CL-Paar anzugeben. + +Die `network_params`-Struktur konfiguriert die Netzwerkeinstellungen, die verwendet werden, um die Genesis-Dateien für jeden Node zu erstellen, sowie andere Einstellungen wie die Sekunden pro Slot des Netzwerks. + +Speichern Sie Ihre bearbeitete Parameterdatei in einem beliebigen Verzeichnis (im folgenden Beispiel wird sie auf dem Desktop gespeichert) und verwenden Sie sie dann, um Ihr Kurtosis-Paket auszuführen, indem Sie Folgendes ausführen: + +```python +kurtosis clean -a && kurtosis run --enclave local-eth-testnet github.com/kurtosis-tech/eth-network-package "$(cat ~/eth-network-params.json)" +``` + +Hinweis: Der Befehl `kurtosis clean -a` wird hier verwendet, um Kurtosis anzuweisen, das alte Testnet und seinen Inhalt zu zerstören, bevor ein neues gestartet wird. + +Auch hier wird Kurtosis eine Weile arbeiten und die einzelnen Schritte ausgeben, die stattfinden. Schließlich sollte die Ausgabe etwa so aussehen: + +```python +Starlark-Code erfolgreich ausgeführt. Keine Ausgabe zurückgegeben. +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 -> http://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 +``` + +Herzlichen Glückwunsch! Sie haben Ihr lokales Testnet erfolgreich so konfiguriert, dass es 3 Nodes anstelle von 1 hat. Um dieselben Arbeitsabläufe wie zuvor mit Ihrer Dapp auszuführen (Bereitstellen und Testen), führen Sie dieselben Operationen wie zuvor durch, indem Sie `<$YOUR_PORT>` in der `localnet`-Struktur Ihrer `hardhat.config.ts`-Konfigurationsdatei durch den Port der RPC-URI-Ausgabe eines beliebigen `el-client-`-Dienstes in Ihrem neuen, 3-Node-lokalen Testnet ersetzen. + +## Fazit {#conclusion} + +Und das war's! Zusammenfassend haben Sie in diesem kurzen Leitfaden: + +- Ein lokales Ethereum-Testnet über Docker mit Kurtosis erstellt +- Ihre lokale Dapp-Entwicklungsumgebung mit dem lokalen Ethereum-Netzwerk verbunden +- Eine Dapp bereitgestellt und einen einfachen Test darauf im lokalen Ethereum-Netzwerk ausgeführt +- Das zugrunde liegende Ethereum-Netzwerk so konfiguriert, dass es 3 Nodes hat + +Wir würden uns freuen, von Ihnen zu hören, was für Sie gut gelaufen ist, was verbessert werden könnte, oder Ihre Fragen zu beantworten. Zögern Sie nicht, uns über [GitHub](https://github.com/kurtosis-tech/kurtosis/issues/new/choose) oder per [E-Mail](mailto:feedback@kurtosistech.com) zu kontaktieren! + +### Weitere Beispiele und Anleitungen {#other-examples-guides} + +Wir empfehlen Ihnen, unseren [Quickstart](https://docs.kurtosis.com/quickstart) (wo Sie eine Postgres-Datenbank und eine API darauf aufbauen) und unsere anderen Beispiele in unserem [awesome-kurtosis Repository](https://github.com/kurtosis-tech/awesome-kurtosis) anzusehen, wo Sie einige großartige Beispiele finden, einschließlich Paketen für: + +- [Starten desselben lokalen Ethereum-Testnets](https://github.com/kurtosis-tech/eth2-package), aber mit zusätzlichen verbundenen Diensten wie einem Transaktions-Spammer (um Transaktionen zu simulieren), einem Fork-Monitor und einer verbundenen Grafana- und Prometheus-Instanz +- Durchführen eines [Sub-Networking-Tests](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/ethereum-network-partition-test) gegen dasselbe lokale Ethereum-Netzwerk diff --git a/public/content/translations/de/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md b/public/content/translations/de/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md new file mode 100644 index 00000000000..36ab458ccfa --- /dev/null +++ b/public/content/translations/de/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md @@ -0,0 +1,144 @@ +--- +title: "Verträge verkleinern, um die Vertragsgrößenbeschränkung zu bekämpfen" +description: "Was kannst du tun, um zu verhindern, dass deine Smart Contracts zu groß werden?" +author: Markus Waas +lang: de +tags: [ "solidity", "intelligente Verträge", "Speicher" ] +skill: intermediate +published: 2020-06-26 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/max-contract-size +--- + +## Warum gibt es ein Limit? {#why-is-there-a-limit} + +Am [22. November 2016](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/) wurde mit dem Spurious Dragon Hard-Fork [EIP-170](https://eips.ethereum.org/EIPS/eip-170) ein Größenlimit von 24.576 kB für Smart Contracts eingeführt. Für dich als Solidity-Entwickler bedeutet dies: Wenn du deinem Vertrag immer mehr Funktionalität hinzufügst, wirst du irgendwann das Limit erreichen und beim Deployment folgenden Fehler sehen: + +`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.` + +Dieses Limit wurde eingeführt, um Denial-of-Service-Angriffe (DOS) zu verhindern. Jeder Aufruf eines Vertrags ist in Bezug auf das Gas relativ günstig. Die Auswirkung eines Vertragsaufrufs für Ethereum-Nodes steigt jedoch in Abhängigkeit von der Größe des aufgerufenen Vertragscodes unverhältnismäßig an (Lesen des Codes von der Festplatte, Vorverarbeitung des Codes, Hinzufügen von Daten zum Merkle-Proof). Immer wenn eine Situation vorliegt, in der der Angreifer nur wenige Ressourcen benötigt, um anderen viel Arbeit zu verursachen, besteht die Gefahr von DOS-Angriffen. + +Ursprünglich war dies kein so großes Problem, da eine natürliche Begrenzung der Vertragsgröße das Block-Gaslimit ist. Ein Vertrag muss natürlich innerhalb einer Transaktion bereitgestellt werden, die den gesamten Bytecode des Vertrags enthält. Wenn du nur diese eine Transaktion in einen Block aufnimmst, kannst du das gesamte Gas verbrauchen, aber es ist nicht unendlich. Seit dem [London-Upgrade](/ethereum-forks/#london) kann das Block-Gaslimit je nach Netzwerkauslastung zwischen 15 und 30 Millionen Einheiten variieren. + +Im Folgenden werden wir uns einige Methoden ansehen, geordnet nach ihrer potenziellen Auswirkung. Stell es dir wie beim Abnehmen vor. Die beste Strategie, um das Zielgewicht zu erreichen (in unserem Fall 24 kB), ist, sich zuerst auf die Methoden mit der größten Wirkung zu konzentrieren. In den meisten Fällen reicht eine Ernährungsumstellung aus, um dieses Ziel zu erreichen, aber manchmal braucht man ein bisschen mehr. Dann könntest du etwas Sport (mittlere Auswirkung) oder sogar Nahrungsergänzungsmittel (geringe Auswirkung) hinzufügen. + +## Große Auswirkung {#big-impact} + +### Trenne deine Verträge {#separate-your-contracts} + +Das sollte immer dein erster Ansatz sein. Wie kannst du den Vertrag in mehrere kleinere aufteilen? Im Allgemeinen zwingt dich das, eine gute Architektur für deine Verträge zu entwickeln. Kleinere Verträge sind aus Sicht der Code-Lesbarkeit immer zu bevorzugen. Stell dir beim Aufteilen von Verträgen folgende Fragen: + +- Welche Funktionen gehören zusammen? Jede Gruppe von Funktionen ist möglicherweise in einem eigenen Vertrag am besten aufgehoben. +- Welche Funktionen erfordern nicht das Lesen des Vertragszustands oder nur einer bestimmten Teilmenge des Zustands? +- Kannst du Speicher und Funktionalität trennen? + +### Bibliotheken {#libraries} + +Eine einfache Möglichkeit, den Funktionalitätscode vom Speicher zu trennen, ist die Verwendung einer [Bibliothek](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries). Deklariere die Bibliotheksfunktionen nicht als „internal“, da diese während der Kompilierung direkt [zum Vertrag hinzugefügt](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking) werden. Wenn du aber „public“-Funktionen verwendest, befinden sich diese tatsächlich in einem separaten Bibliotheksvertrag. Erwäge die Verwendung von „[using for](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for)“, um die Nutzung von Bibliotheken komfortabler zu gestalten. + +### Proxys {#proxies} + +Eine fortgeschrittenere Strategie wäre ein Proxysystem. Bibliotheken verwenden im Hintergrund `DELEGATECALL`, was einfach die Funktion eines anderen Vertrags mit dem Zustand des aufrufenden Vertrags ausführt. In [diesem Blogbeitrag](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2) erfährst du mehr über Proxysysteme. Sie bieten dir mehr Funktionalität, z. B. ermöglichen sie die Upgradefähigkeit, aber sie fügen auch eine Menge Komplexität hinzu. Ich würde sie nicht nur zur Reduzierung der Vertragsgröße hinzufügen, es sei denn, es ist aus irgendeinem Grund deine einzige Option. + +## Mittlere Auswirkung {#medium-impact} + +### Funktionen entfernen {#remove-functions} + +Das sollte selbsterklärend sein. Funktionen vergrößern einen Vertrag erheblich. + +- **External**: Oft fügen wir aus Bequemlichkeitsgründen viele Ansichtsfunktionen hinzu. Das ist völlig in Ordnung, bis du an das Größenlimit stößt. Dann solltest du wirklich darüber nachdenken, alle bis auf die absolut notwendigen zu entfernen. +- **Internal**: Du kannst auch interne/private Funktionen entfernen und den Code einfach inline einfügen, solange die Funktion nur einmal aufgerufen wird. + +### Vermeide zusätzliche Variablen {#avoid-additional-variables} + +```solidity +function get(uint id) returns (address,address) { + MyStruct memory myStruct = myStructs[id]; + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns (address,address) { + return (myStructs[id].addr1, myStructs[id].addr2); +} +``` + +Eine einfache Änderung wie diese macht einen Unterschied von **0,28 kB** aus. Wahrscheinlich findest du viele ähnliche Situationen in deinen Verträgen, und diese können sich wirklich zu erheblichen Mengen summieren. + +### Fehlermeldungen kürzen {#shorten-error-message} + +Lange „revert“-Meldungen und insbesondere viele verschiedene „revert“-Meldungen können den Vertrag aufblähen. Verwende stattdessen kurze Fehlercodes und dekodiere sie in deinem Vertrag. Eine lange Meldung könnte viel kürzer werden: + +```solidity +require(msg.sender == owner, "Nur der Besitzer dieses Vertrags kann diese Funktion aufrufen"); +``` + +```solidity +require(msg.sender == owner, "OW1"); +``` + +### Verwende benutzerdefinierte Fehler anstelle von Fehlermeldungen + +Benutzerdefinierte Fehler wurden in [Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/) eingeführt. Sie sind eine großartige Möglichkeit, die Größe deiner Verträge zu reduzieren, da sie als Selektoren ABI-kodiert werden (genau wie Funktionen). + +```solidity +error Unauthorized(); + +if (msg.sender != owner) { + revert Unauthorized(); +} +``` + +### Ziehe einen niedrigen „run“-Wert im Optimizer in Betracht {#consider-a-low-run-value-in-the-optimizer} + +Du kannst auch die Optimizer-Einstellungen ändern. Der Standardwert von 200 bedeutet, dass er versucht, den Bytecode so zu optimieren, als ob eine Funktion 200 Mal aufgerufen würde. Wenn du ihn auf 1 änderst, weist du den Optimizer im Grunde an, für den Fall zu optimieren, dass jede Funktion nur einmal ausgeführt wird. Eine für die einmalige Ausführung optimierte Funktion bedeutet, dass sie für die Bereitstellung selbst optimiert ist. Beachte, dass **dies die [Gaskosten](/developers/docs/gas/) für die Ausführung der Funktionen erhöht**, also möchtest du das vielleicht nicht tun. + +## Geringe Auswirkung {#small-impact} + +### Vermeide die Übergabe von Structs an Funktionen {#avoid-passing-structs-to-functions} + +Wenn du den [ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2) verwendest, kann es helfen, keine Structs an eine Funktion zu übergeben. Anstatt den Parameter als Struct zu übergeben, übergib die erforderlichen Parameter direkt. In diesem Beispiel haben wir weitere **0,1 kB** gespart. + +```solidity +function get(uint id) returns (address,address) { + return _get(myStruct); +} + +function _get(MyStruct memory myStruct) private view returns(address,address) { + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns(address,address) { + return _get(myStructs[id].addr1, myStructs[id].addr2); +} + +function _get(address addr1, address addr2) private view returns(address,address) { + return (addr1, addr2); +} +``` + +### Korrekte Sichtbarkeit für Funktionen und Variablen deklarieren {#declare-correct-visibility-for-functions-and-variables} + +- Funktionen oder Variablen, die nur von außen aufgerufen werden? Deklariere sie als `external` anstatt `public`. +- Funktionen oder Variablen, die nur innerhalb des Vertrags aufgerufen werden? Deklariere sie als `private` oder `internal` anstatt `public`. + +### Modifier entfernen {#remove-modifiers} + +Modifier können, insbesondere bei intensiver Nutzung, einen erheblichen Einfluss auf die Vertragsgröße haben. Erwäge, sie zu entfernen und stattdessen Funktionen zu verwenden. + +```solidity +modifier checkStuff() {} + +function doSomething() checkStuff {} +``` + +```solidity +function checkStuff() private {} + +function doSomething() { checkStuff(); } +``` + +Diese Tipps sollten dir helfen, die Vertragsgröße erheblich zu reduzieren. Noch einmal, ich kann nicht genug betonen: Konzentriere dich immer auf die Aufteilung von Verträgen, wenn möglich, um die größte Wirkung zu erzielen. diff --git a/public/content/translations/de/developers/tutorials/eip-1271-smart-contract-signatures/index.md b/public/content/translations/de/developers/tutorials/eip-1271-smart-contract-signatures/index.md new file mode 100644 index 00000000000..79eb492e35a --- /dev/null +++ b/public/content/translations/de/developers/tutorials/eip-1271-smart-contract-signatures/index.md @@ -0,0 +1,129 @@ +--- +title: "EIP-1271: Signieren und Verifizieren von Smart-Contract-Signaturen" +description: "Ein Überblick über die Erstellung und Verifizierung von Smart-Contract-Signaturen mit EIP-1271. Wir gehen auch die in Safe (ehemals Gnosis Safe) verwendete EIP-1271-Implementierung durch, um Smart-Contract-Entwicklern ein konkretes Beispiel zu geben, auf dem sie aufbauen können." +author: Nathan H. Leung +lang: de +tags: + [ + "eip-1271", + "Smart Contracts", + "verifizieren", + "Signieren" + ] +skill: intermediate +published: 2023-01-12 +--- + +Der [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)-Standard ermöglicht es Smart Contracts, Signaturen zu verifizieren. + +In diesem Tutorial geben wir einen Überblick über digitale Signaturen, den Hintergrund von EIP-1271 und die spezifische Implementierung von EIP-1271, die von [Safe](https://safe.global/) (ehemals Gnosis Safe) verwendet wird. Alles in allem kann dies als Ausgangspunkt für die Implementierung von EIP-1271 in Ihren eigenen Contracts dienen. + +## Was ist eine Signatur? + +In diesem Kontext ist eine Signatur (genauer gesagt, eine „digitale Signatur“) eine Nachricht plus eine Art Beweis dafür, dass die Nachricht von einer bestimmten Person/einem bestimmten Absender/einer bestimmten Adresse stammt. + +Eine digitale Signatur könnte zum Beispiel so aussehen: + +1. Nachricht: „Ich möchte mich mit meiner Ethereum-Wallet auf dieser Website anmelden.“ +2. Unterzeichner: Meine Adresse lautet `0x000…` +3. Beweis: Hier ist ein Beweis dafür, dass ich, `0x000…`, diese gesamte Nachricht tatsächlich erstellt habe (dies ist normalerweise etwas Kryptografisches). + +Es ist wichtig zu beachten, dass eine digitale Signatur sowohl eine „Nachricht“ als auch eine „Signatur“ enthält. + +Warum? Wenn Sie mir zum Beispiel einen Vertrag zum Unterschreiben geben würden und ich dann die Unterschriftenseite abschneiden und Ihnen nur meine Unterschriften ohne den Rest des Vertrags zurückgeben würde, wäre der Vertrag nicht gültig. + +Genauso bedeutet eine digitale Signatur ohne eine zugehörige Nachricht nichts! + +## Warum gibt es EIP-1271? + +Um eine digitale Signatur für die Verwendung auf Ethereum-basierten Blockchains zu erstellen, benötigen Sie im Allgemeinen einen geheimen privaten Schlüssel, den niemand sonst kennt. Das ist es, was Ihre Signatur zu Ihrer macht (niemand sonst kann dieselbe Signatur ohne Kenntnis des geheimen Schlüssels erstellen). + +Ihr Ethereum-Konto (d.h. Ihr extern verwaltetes Konto/EOA) hat einen damit verbundenen privaten Schlüssel, und das ist der private Schlüssel, der normalerweise verwendet wird, wenn eine Website oder eine Dapp Sie um eine Signatur bittet (z. B. für „Mit Ethereum anmelden“). + +Eine App kann eine von Ihnen erstellte [Signatur verifizieren](https://www.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum) und dabei eine Drittanbieter-Bibliothek wie ethers.js verwenden, [ohne Ihren privaten Schlüssel zu kennen](https://en.wikipedia.org/wiki/Public-key_cryptography), und darauf vertrauen, dass _Sie_ derjenige waren, der die Signatur erstellt hat. + +> Tatsächlich können digitale EOA-Signaturen, da sie Public-Key-Kryptografie verwenden, **off-chain** generiert und verifiziert werden! So funktioniert die gaslose DAO-Abstimmung — anstatt die Stimmen on-chain abzugeben, können digitale Signaturen mithilfe kryptografischer Bibliotheken off-chain erstellt und verifiziert werden. + +Während EOA-Konten einen privaten Schlüssel haben, haben Smart-Contract-Konten keinerlei privaten oder geheimen Schlüssel (daher kann „Mit Ethereum anmelden“ usw. nicht nativ mit Smart-Contract-Konten funktionieren). + +Das Problem, das EIP-1271 zu lösen versucht: Wie können wir feststellen, ob eine Smart-Contract-Signatur gültig ist, wenn der Smart Contract kein „Geheimnis“ hat, das er in die Signatur einbauen kann? + +## Wie funktioniert EIP-1271? + +Smart Contracts haben keine privaten Schlüssel, die zum Signieren von Nachrichten verwendet werden können. Wie können wir also feststellen, ob eine Signatur authentisch ist? + +Nun, eine Idee ist, dass wir den Smart Contract einfach _fragen_ können, ob eine Signatur authentisch ist! + +EIP-1271 standardisiert die Idee, einen Smart Contract zu „fragen“, ob eine bestimmte Signatur gültig ist. + +Ein Contract, der EIP-1271 implementiert, muss eine Funktion namens `isValidSignature` haben, die eine Nachricht und eine Signatur entgegennimmt. Der Contract kann dann eine Validierungslogik ausführen (die Spezifikation schreibt hier nichts Bestimmtes vor) und dann einen Wert zurückgeben, der angibt, ob die Signatur gültig ist oder nicht. + +Wenn `isValidSignature` ein gültiges Ergebnis zurückgibt, bedeutet das so viel wie, dass der Contract sagt: „Ja, ich genehmige diese Signatur + Nachricht!“ + +### Schnittstelle + +Hier ist die genaue Schnittstelle in der EIP-1271-Spezifikation (wir werden weiter unten auf den `_hash`-Parameter eingehen, aber stellen Sie ihn sich vorerst als die zu verifizierende Nachricht vor): + +```jsx +pragma solidity ^0.5.0; + +contract ERC1271 { + + // bytes4(keccak256("isValidSignature(bytes32,bytes)") + bytes4 constant internal MAGICVALUE = 0x1626ba7e; + + /** + * @dev Sollte zurückgeben, ob die bereitgestellte Signatur für den bereitgestellten Hash gültig ist + * @param _hash Hash der zu signierenden Daten + * @param _signature Signatur-Byte-Array, das mit _hash verknüpft ist + * + * MUSS den magischen Wert bytes4 0x1626ba7e zurückgeben, wenn die Funktion erfolgreich ist. + * DARF den Zustand NICHT ändern (Verwendung von STATICCALL für solc < 0.5, view-Modifikator für solc > 0.5) + * MUSS externe Aufrufe zulassen + */ + function isValidSignature( + bytes32 _hash, + bytes memory _signature) + public + view + returns (bytes4 magicValue); +} +``` + +## Beispiel für eine EIP-1271-Implementierung: Safe + +Contracts können `isValidSignature` auf viele Arten implementieren — die Spezifikation sagt nicht viel über die genaue Implementierung aus. + +Ein bemerkenswerter Contract, der EIP-1271 implementiert, ist Safe (ehemals Gnosis Safe). + +Im Code von Safe ist `isValidSignature` [so implementiert](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol), dass Signaturen auf [zwei Arten](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support) erstellt und verifiziert werden können: + +1. On-Chain-Nachrichten + 1. Erstellung: Ein Safe-Besitzer erstellt eine neue Safe-Transaktion, um eine Nachricht zu „signieren“, und übergibt die Nachricht als Daten in die Transaktion. Sobald genügend Besitzer die Transaktion unterzeichnet haben, um den Multisig-Schwellenwert zu erreichen, wird die Transaktion gesendet und ausgeführt. In der Transaktion gibt es eine Safe-Funktion namens (`signMessage(bytes calldata _data)`), die die Nachricht zu einer Liste „genehmigter“ Nachrichten hinzufügt. + 2. Verifizierung: Rufen Sie `isValidSignature` auf dem Safe-Contract auf und übergeben Sie die zu verifizierende Nachricht als Nachrichtenparameter und [einen leeren Wert für den Signaturparameter](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (d. h. `0x`). Der Safe wird sehen, dass der Signaturparameter leer ist, und anstatt die Signatur kryptografisch zu verifizieren, wird er einfach prüfen, ob sich die Nachricht auf der Liste der „genehmigten“ Nachrichten befindet. +2. Off-Chain-Nachrichten: + 1. Erstellung: Ein Safe-Besitzer erstellt eine Nachricht off-chain und lässt dann andere Safe-Besitzer die Nachricht einzeln signieren, bis genügend Signaturen vorhanden sind, um den Multisig-Genehmigungsschwellenwert zu erreichen. + 2. Verifizierung: `isValidSignature` aufrufen. Übergeben Sie im Nachrichtenparameter die zu verifizierende Nachricht. Übergeben Sie im Signaturparameter die einzelnen Signaturen jedes Safe-Besitzers aneinandergereiht. Der Safe prüft, ob genügend Signaturen vorhanden sind, um den Schwellenwert zu erreichen, **und** ob jede Signatur gültig ist. Wenn ja, gibt er einen Wert zurück, der eine erfolgreiche Signaturverifizierung anzeigt. + +## Was genau ist der `_hash`-Parameter? Warum nicht die ganze Nachricht übergeben? + +Sie haben vielleicht bemerkt, dass die Funktion `isValidSignature` in der [EIP-1271-Schnittstelle](https://eips.ethereum.org/EIPS/eip-1271) nicht die Nachricht selbst, sondern einen `_hash`-Parameter entgegennimmt. Das bedeutet, dass wir anstelle der vollständigen Nachricht beliebiger Länge an `isValidSignature` einen 32-Byte-Hash der Nachricht (im Allgemeinen keccak256) übergeben. + +Jedes Byte Calldata — d. h. Funktionsparameterdaten, die an eine Smart-Contract-Funktion übergeben werden — [kostet 16 Gas (4 Gas bei einem Null-Byte)](https://eips.ethereum.org/EIPS/eip-2028), sodass viel Gas gespart werden kann, wenn eine Nachricht lang ist. + +### Frühere EIP-1271-Spezifikationen + +Es gibt EIP-1271-Spezifikationen, die eine `isValidSignature`-Funktion mit einem ersten Parameter vom Typ `bytes` (beliebige Länge anstelle einer festen Länge von `bytes32`) und dem Parameternamen `message` haben. Dies ist eine [ältere Version](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206) des EIP-1271-Standards. + +## Wie sollte EIP-1271 in meinen eigenen Contracts implementiert werden? + +Die Spezifikation ist hier sehr offen. Die Safe-Implementierung hat einige gute Ideen: + +- Sie können EOA-Signaturen vom „Besitzer“ des Contracts als gültig betrachten. +- Sie könnten eine Liste genehmigter Nachrichten speichern und nur diese als gültig betrachten. + +Letztendlich liegt es an Ihnen als Contract-Entwickler! + +## Zusammenfassung + +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) ist ein vielseitiger Standard, der es Smart Contracts ermöglicht, Signaturen zu verifizieren. Es öffnet Smart Contracts die Tür, sich mehr wie EOAs zu verhalten – indem es beispielsweise eine Möglichkeit bietet, dass „Mit Ethereum anmelden“ mit Smart Contracts funktioniert – und es kann auf viele Arten implementiert werden (Safe bietet eine nichttriviale und interessante Implementierung, die man in Betracht ziehen sollte). diff --git a/public/content/translations/de/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/translations/de/developers/tutorials/erc-721-vyper-annotated-code/index.md new file mode 100644 index 00000000000..d627213ef79 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -0,0 +1,716 @@ +--- +title: "Vyper ERC-721 Vertrags-Komplettlösung" +description: Ryuya Nakamuras ERC-721-Vertrag und wie er funktioniert +author: Ori Pomerantz +lang: de +tags: [ "vyper", "erc-721", "Python" ] +skill: beginner +published: 2021-04-01 +--- + +## Einführung {#introduction} + +Der [ERC-721](/developers/docs/standards/tokens/erc-721/)-Standard wird verwendet, um das Eigentum an nicht-fungiblen Token (NFT) zu halten. +[ERC-20](/developers/docs/standards/tokens/erc-20/)-Token verhalten sich wie ein Rohstoff, da es keinen Unterschied zwischen den einzelnen Token gibt. +Im Gegensatz dazu sind ERC-721-Token für Vermögenswerte konzipiert, die ähnlich, aber nicht identisch sind, wie zum Beispiel verschiedene Katzen- +Cartoons +oder Titel für verschiedene Immobilien. + +In diesem Artikel analysieren wir [Ryuya Nakamuras ERC-721-Vertrag](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy). +Dieser Vertrag ist in [Vyper](https://vyper.readthedocs.io/en/latest/index.html) geschrieben, einer Python-ähnlichen Vertragssprache, die so konzipiert ist, dass es +schwieriger ist, unsicheren Code zu schreiben, als in Solidity. + +## Der Vertrag {#contract} + +```python +# @dev Implementierung des ERC-721-Standards für nicht-fungible Token. +# @author Ryuya Nakamura (@nrryuya) +# Modifiziert von: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy +``` + +Kommentare in Vyper beginnen, wie in Python, mit einem Hash (`#`) und gehen bis zum Ende der Zeile. Kommentare, die +`@` enthalten, werden von [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) verwendet, um für Menschen lesbare +Dokumentationen zu erstellen. + +```python +from vyper.interfaces import ERC721 + +implements: ERC721 +``` + +Die ERC-721-Schnittstelle ist in die Vyper-Sprache integriert. +[Die Code-Definition können Sie hier einsehen](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py). +Die Schnittstellendefinition ist in Python und nicht in Vyper geschrieben, da Schnittstellen nicht nur innerhalb der +Blockchain verwendet werden, sondern auch beim Senden einer Transaktion von einem externen Client an die Blockchain, der in +Python geschrieben sein kann. + +Die erste Zeile importiert die Schnittstelle und die zweite gibt an, dass wir sie hier implementieren. + +### Die ERC721Receiver-Schnittstelle {#receiver-interface} + +```python +# Schnittstelle für den von safeTransferFrom() aufgerufenen Vertrag +interface ERC721Receiver: + def onERC721Received( +``` + +ERC-721 unterstützt zwei Arten von Übertragungen: + +- `transferFrom`, bei dem der Absender eine beliebige Zieladresse angeben kann und die Verantwortung + für die Übertragung beim Absender liegt. Das bedeutet, dass Sie an eine ungültige Adresse übertragen können. In diesem Fall + ist der NFT für immer verloren. +- `safeTransferFrom`, das prüft, ob die Zieladresse ein Vertrag ist. Wenn ja, fragt der ERC-721-Vertrag + den empfangenden Vertrag, ob er den NFT empfangen möchte. + +Um `safeTransferFrom`-Anfragen zu beantworten, muss ein empfangender Vertrag `ERC721Receiver` implementieren. + +```python + _operator: address, + _from: address, +``` + +Die `_from`-Adresse ist der aktuelle Eigentümer des Tokens. Die `_operator`-Adresse ist diejenige, die +die Übertragung angefordert hat (diese beiden können aufgrund von Freigaben unterschiedlich sein). + +```python + _tokenId: uint256, +``` + +ERC-721-Token-IDs sind 256 Bit lang. Typischerweise werden sie durch Hashing einer Beschreibung von dem erstellt, was +der Token darstellt. + +```python + _data: Bytes[1024] +``` + +Die Anfrage kann bis zu 1024 Bytes an Benutzerdaten enthalten. + +```python + ) -> bytes32: view +``` + +Um zu verhindern, dass ein Vertrag versehentlich eine Übertragung akzeptiert, ist der Rückgabewert kein boolescher Wert, +sondern 256 Bit mit einem bestimmten Wert. + +Diese Funktion ist eine `view`, was bedeutet, dass sie den Zustand der Blockchain lesen, aber nicht verändern kann. + +### Ereignisse {#events} + +[Ereignisse](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e) +werden ausgelöst, um Benutzer und Server außerhalb der Blockchain über Ereignisse zu informieren. Beachten Sie, dass der Inhalt von Ereignissen +für Verträge auf der Blockchain nicht verfügbar ist. + +```python +# @dev Wird ausgelöst, wenn sich der Besitz eines NFTs durch einen beliebigen Mechanismus ändert. Dieses Ereignis wird ausgelöst, wenn NFTs +# erstellt (`from` == 0) und zerstört (`to` == 0) werden. Ausnahme: während der Vertragserstellung kann eine beliebige +# Anzahl von NFTs erstellt und zugewiesen werden, ohne dass ein Transfer ausgelöst wird. Zum Zeitpunkt einer +# Übertragung wird die genehmigte Adresse für diesen NFT (falls vorhanden) auf keine zurückgesetzt. +# @param _from Absender des NFT (wenn die Adresse eine Null-Adresse ist, bedeutet das die Erstellung eines Tokens). +# @param _to Empfänger des NFT (wenn die Adresse eine Null-Adresse ist, bedeutet das die Zerstörung des Tokens). +# @param _tokenId Der NFT, der übertragen wurde. +event Transfer: + sender: indexed(address) + receiver: indexed(address) + tokenId: indexed(uint256) +``` + +Dies ist ähnlich wie beim ERC-20-Transfer-Ereignis, außer dass wir eine `tokenId` anstelle eines Betrags melden. +Niemand besitzt die Adresse Null, daher verwenden wir sie konventionsgemäß, um die Erstellung und Zerstörung von Token zu melden. + +```python +# @dev Dies wird ausgelöst, wenn die genehmigte Adresse für einen NFT geändert oder erneut bestätigt wird. Die Null- +# Adresse zeigt an, dass es keine genehmigte Adresse gibt. Wenn ein Transfer-Ereignis ausgelöst wird, zeigt dies auch an +# , dass die genehmigte Adresse für diesen NFT (falls vorhanden) auf keine zurückgesetzt wird. +# @param _owner Eigentümer des NFT. +# @param _approved Adresse, die wir genehmigen. +# @param _tokenId NFT, den wir genehmigen. +event Approval: + owner: indexed(address) + approved: indexed(address) + tokenId: indexed(uint256) +``` + +Eine ERC-721-Genehmigung ist ähnlich wie eine ERC-20-Freigabe. Eine bestimmte Adresse darf einen bestimmten +Token übertragen. Dies gibt Verträgen einen Mechanismus, um zu reagieren, wenn sie einen Token annehmen. Verträge können nicht +auf Ereignisse lauschen, wenn Sie also nur den Token an sie übertragen, „wissen“ sie nichts davon. Auf diese Weise reicht der +Eigentümer zunächst eine Genehmigung ein und sendet dann eine Anfrage an den Vertrag: „Ich habe Ihnen die Genehmigung erteilt, den Token +X zu übertragen, bitte tun Sie ...“. + +Dies ist eine Designentscheidung, um den ERC-721-Standard dem ERC-20-Standard ähnlich zu machen. Da +ERC-721-Token nicht fungibel sind, kann ein Vertrag auch erkennen, dass er einen bestimmten Token erhalten hat, indem er sich den Besitz des Tokens +ansieht. + +```python +# @dev Dies wird ausgelöst, wenn ein Betreiber für einen Eigentümer aktiviert oder deaktiviert wird. Der Betreiber kann alle NFTs des Eigentümers verwalten. +# +# @param _owner Eigentümer des NFT. +# @param _operator Adresse, für die wir Betreiberrechte festlegen. +# @param _approved Status der Betreiberrechte (true, wenn Betreiberrechte erteilt werden, und false, wenn sie +# widerrufen werden). +event ApprovalForAll: + owner: indexed(address) + operator: indexed(address) + approved: bool +``` + +Manchmal ist es nützlich, einen _Betreiber_ zu haben, der alle Token eines Kontos von einem bestimmten Typ verwalten kann (diejenigen, die von +einem bestimmten Vertrag verwaltet werden), ähnlich wie eine Vollmacht. Zum Beispiel könnte ich einem Vertrag eine solche Vollmacht erteilen, der prüft, ob +ich ihn sechs Monate lang nicht kontaktiert habe, und wenn ja, mein Vermögen an meine Erben verteilt (wenn einer von ihnen danach fragt, können Verträge +nichts tun, ohne durch eine Transaktion aufgerufen zu werden). Bei ERC-20 können wir einem Erbvertrag einfach eine hohe Freigabe erteilen, +aber das funktioniert bei ERC-721 nicht, da die Token nicht fungibel sind. Dies ist das Äquivalent. + +Der `approved`-Wert teilt uns mit, ob das Ereignis eine Genehmigung oder den Widerruf einer Genehmigung betrifft. + +### Zustandsvariablen {#state-vars} + +Diese Variablen enthalten den aktuellen Zustand der Token: welche verfügbar sind und wem sie gehören. Die meisten davon +sind `HashMap`-Objekte, [unidirektionale Zuordnungen, die zwischen zwei Typen bestehen](https://vyper.readthedocs.io/en/latest/types.html#mappings). + +```python +# @dev Zuordnung von der NFT-ID zur Adresse, der sie gehört. +idToOwner: HashMap[uint256, address] + +# @dev Zuordnung von der NFT-ID zur genehmigten Adresse. +idToApprovals: HashMap[uint256, address] +``` + +Benutzer- und Vertragsidentitäten in Ethereum werden durch 160-Bit-Adressen dargestellt. Diese beiden Variablen bilden +Token-IDs auf ihre Eigentümer und diejenigen ab, die für ihre Übertragung genehmigt sind (maximal einer für jeden). In Ethereum +sind nicht initialisierte Daten immer null. Wenn es also keinen Eigentümer oder genehmigten Überträger gibt, ist der Wert für diesen Token +null. + +```python +# @dev Zuordnung von der Eigentümeradresse zur Anzahl seiner Token. +ownerToNFTokenCount: HashMap[address, uint256] +``` + +Diese Variable enthält die Anzahl der Token für jeden Eigentümer. Es gibt keine Zuordnung von Eigentümern zu Token, daher +ist der einzige Weg, die Token zu identifizieren, die einem bestimmten Eigentümer gehören, der Blick zurück in die Ereignishistorie der Blockchain, +um die entsprechenden `Transfer`-Ereignisse zu sehen. Wir können diese Variable verwenden, um zu wissen, wann wir alle NFTs haben und nicht +noch weiter in die Vergangenheit blicken müssen. + +Beachten Sie, dass dieser Algorithmus nur für Benutzeroberflächen und externe Server funktioniert. Code, der auf der Blockchain +selbst läuft, kann vergangene Ereignisse nicht lesen. + +```python +# @dev Zuordnung von der Eigentümeradresse zur Zuordnung von Betreiberadressen. +ownerToOperators: HashMap[address, HashMap[address, bool]] +``` + +Ein Konto kann mehr als einen Betreiber haben. Eine einfache `HashMap` reicht nicht aus, um +sie zu verfolgen, da jeder Schlüssel zu einem einzigen Wert führt. Stattdessen können Sie +`HashMap[address, bool]` als Wert verwenden. Standardmäßig ist der Wert für jede Adresse `False`, was bedeutet, dass sie +kein Betreiber ist. Sie können die Werte bei Bedarf auf `True` setzen. + +```python +# @dev Adresse des Minters, der einen Token prägen kann +minter: address +``` + +Neue Token müssen irgendwie erstellt werden. In diesem Vertrag gibt es eine einzige Entität, die dazu berechtigt ist, der +`minter`. Dies ist zum Beispiel für ein Spiel wahrscheinlich ausreichend. Für andere Zwecke könnte es notwendig sein, +eine kompliziertere Geschäftslogik zu erstellen. + +```python +# @dev Zuordnung der Schnittstellen-ID zu bool, ob sie unterstützt wird oder nicht +supportedInterfaces: HashMap[bytes32, bool] + +# @dev ERC165-Schnittstellen-ID von ERC165 +ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7 + +# @dev ERC165-Schnittstellen-ID von ERC721 +ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd +``` + +[ERC-165](https://eips.ethereum.org/EIPS/eip-165) spezifiziert einen Mechanismus für einen Vertrag, um offenzulegen, wie Anwendungen +mit ihm kommunizieren können und welchen ERCs er entspricht. In diesem Fall entspricht der Vertrag ERC-165 und ERC-721. + +### Funktionen {#functions} + +Dies sind die Funktionen, die ERC-721 tatsächlich implementieren. + +#### Konstruktor {#constructor} + +```python +@external +def __init__(): +``` + +In Vyper, wie in Python, heißt die Konstruktorfunktion `__init__`. + +```python + """ + @dev Vertragskonstruktor. + """ +``` + +In Python und in Vyper können Sie auch einen Kommentar erstellen, indem Sie eine mehrzeilige Zeichenfolge (die mit `"""` beginnt und endet +) angeben und sie in keiner Weise verwenden. Diese Kommentare können auch +[NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) enthalten. + +```python + self.supportedInterfaces[ERC165_INTERFACE_ID] = True + self.supportedInterfaces[ERC721_INTERFACE_ID] = True + self.minter = msg.sender +``` + +Um auf Zustandsvariablen zuzugreifen, verwenden Sie `self.`` (wiederum, wie in Python). + +#### View-Funktionen {#views} + +Dies sind Funktionen, die den Zustand der Blockchain nicht verändern und daher +kostenlos ausgeführt werden können, wenn sie extern aufgerufen werden. Wenn die View-Funktionen von einem Vertrag aufgerufen werden, müssen sie dennoch auf +jedem Node ausgeführt werden und kosten daher Gas. + +```python +@view +@external +``` + +Diese Schlüsselwörter vor einer Funktionsdefinition, die mit einem At-Zeichen (`@`) beginnen, werden als _Dekorationen_ bezeichnet. Sie +geben die Umstände an, unter denen eine Funktion aufgerufen werden kann. + +- `@view` gibt an, dass diese Funktion eine `view` ist. +- `@external` gibt an, dass diese spezielle Funktion durch Transaktionen und von anderen Verträgen aufgerufen werden kann. + +```python +def supportsInterface(_interfaceID: bytes32) -> bool: +``` + +Im Gegensatz zu Python ist Vyper eine [statisch typisierte Sprache](https://wikipedia.org/wiki/Type_system#Static_type_checking). +Sie können keine Variable oder einen Funktionsparameter deklarieren, ohne den [Datentyp](https://vyper.readthedocs.io/en/latest/types.html) anzugeben. In diesem Fall ist der Eingabeparameter `bytes32`, ein 256-Bit-Wert +(256 Bit ist die native Wortgröße der [Ethereum Virtual Machine](/developers/docs/evm/)). Die Ausgabe ist ein boolescher +Wert. Konventionsgemäß beginnen die Namen von Funktionsparametern mit einem Unterstrich (`_`). + +```python + """ + @dev Die Schnittstellenidentifikation ist in ERC-165 spezifiziert. + @param _interfaceID ID der Schnittstelle + """ + return self.supportedInterfaces[_interfaceID] +``` + +Gibt den Wert aus der `self.supportedInterfaces`-HashMap zurück, der im Konstruktor (`__init__`) gesetzt wird. + +```python +### VIEW-FUNKTIONEN ### + +``` + +Dies sind die View-Funktionen, die Informationen über die Token für Benutzer und andere Verträge verfügbar machen. + +```python +@view +@external +def balanceOf(_owner: address) -> uint256: + """ + @dev Gibt die Anzahl der NFTs zurück, die `_owner` besitzt. + Löst einen Fehler aus, wenn `_owner` die Null-Adresse ist. NFTs, die der Null-Adresse zugewiesen sind, werden als ungültig betrachtet. + @param _owner Adresse, für die das Guthaben abgefragt werden soll. + """ + assert _owner != ZERO_ADDRESS +``` + +Diese Zeile [stellt sicher](https://vyper.readthedocs.io/en/latest/statements.html#assert), dass `_owner` nicht +Null ist. Wenn doch, gibt es einen Fehler und die Operation wird rückgängig gemacht. + +```python + return self.ownerToNFTokenCount[_owner] + +@view +@external +def ownerOf(_tokenId: uint256) -> address: + """ + @dev Gibt die Adresse des Besitzers des NFT zurück. + Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist. + @param _tokenId Der Bezeichner für einen NFT. + """ + owner: address = self.idToOwner[_tokenId] + # Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist + assert owner != ZERO_ADDRESS + return owner +``` + +In der Ethereum Virtual Machine (EVM) ist jeder Speicher, in dem kein Wert gespeichert ist, null. +Wenn es keinen Token bei `_tokenId` gibt, dann ist der Wert von `self.idToOwner[_tokenId]` null. In diesem +Fall wird die Funktion zurückgesetzt. + +```python +@view +@external +def getApproved(_tokenId: uint256) -> address: + """ + @dev Holt die genehmigte Adresse für einen einzelnen NFT. + Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist. + @param _tokenId ID des NFT, dessen Genehmigung abgefragt werden soll. + """ + # Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist + assert self.idToOwner[_tokenId] != ZERO_ADDRESS + return self.idToApprovals[_tokenId] +``` + +Beachten Sie, dass `getApproved` null zurückgeben _kann_. Wenn der Token gültig ist, gibt er `self.idToApprovals[_tokenId]` zurück. +Wenn es keinen Genehmiger gibt, ist dieser Wert null. + +```python +@view +@external +def isApprovedForAll(_owner: address, _operator: address) -> bool: + """ + @dev Prüft, ob `_operator` ein zugelassener Betreiber für `_owner` ist. + @param _owner Die Adresse, der die NFTs gehören. + @param _operator Die Adresse, die im Namen des Eigentümers handelt. + """ + return (self.ownerToOperators[_owner])[_operator] +``` + +Diese Funktion prüft, ob `_operator` berechtigt ist, alle Token von `_owner` in diesem Vertrag zu verwalten. +Da es mehrere Operatoren geben kann, ist dies eine zweistufige HashMap. + +#### Transfer-Hilfsfunktionen {#transfer-helpers} + +Diese Funktionen implementieren Operationen, die Teil der Übertragung oder Verwaltung von Token sind. + +```python + +### HILFSFUNKTIONEN FÜR DIE ÜBERTRAGUNG ### + +@view +@internal +``` + +Diese Dekoration, `@internal`, bedeutet, dass die Funktion nur von anderen Funktionen innerhalb desselben +Vertrags aus zugänglich ist. Konventionsgemäß beginnen diese Funktionsnamen ebenfalls mit einem Unterstrich (`_`). + +```python +def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: + """ + @dev Gibt zurück, ob der angegebene Spender eine gegebene Token-ID übertragen kann + @param spender Adresse des abzufragenden Spenders + @param tokenId uint256 ID des zu übertragenden Tokens + @return bool, ob der msg.sender für die angegebene Token-ID genehmigt ist, + ein Betreiber des Eigentümers oder der Eigentümer des Tokens ist + """ + owner: address = self.idToOwner[_tokenId] + spenderIsOwner: bool = owner == _spender + spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId] + spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender] + return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll +``` + +Es gibt drei Möglichkeiten, wie eine Adresse zur Übertragung eines Tokens berechtigt sein kann: + +1. Die Adresse ist der Eigentümer des Tokens +2. Die Adresse ist für die Ausgabe dieses Tokens zugelassen +3. Die Adresse ist ein Operator für den Eigentümer des Tokens + +Die obige Funktion kann eine `view` sein, da sie den Zustand nicht ändert. Um die Betriebskosten zu senken, sollte jede +Funktion, die eine `view` sein _kann_, auch eine `view` _sein_. + +```python +@internal +def _addTokenTo(_to: address, _tokenId: uint256): + """ + @dev Einen NFT zu einer bestimmten Adresse hinzufügen + Löst einen Fehler aus, wenn `_tokenId` jemandem gehört. + """ + # Löst einen Fehler aus, wenn `_tokenId` jemandem gehört + assert self.idToOwner[_tokenId] == ZERO_ADDRESS + # Den Besitzer wechseln + self.idToOwner[_tokenId] = _to + # Zählverfolgung ändern + self.ownerToNFTokenCount[_to] += 1 + + +@internal +def _removeTokenFrom(_from: address, _tokenId: uint256): + """ + @dev Entfernen eines NFT von einer bestimmten Adresse + Löst einen Fehler aus, wenn `_from` nicht der aktuelle Besitzer ist. + """ + # Löst einen Fehler aus, wenn `_from` nicht der aktuelle Besitzer ist + assert self.idToOwner[_tokenId] == _from + # Den Besitzer wechseln + self.idToOwner[_tokenId] = ZERO_ADDRESS + # Zählverfolgung ändern + self.ownerToNFTokenCount[_from] -= 1 +``` + +Wenn es ein Problem mit einer Übertragung gibt, machen wir den Aufruf rückgängig. + +```python +@internal +def _clearApproval(_owner: address, _tokenId: uint256): + """ + @dev Löschen einer Genehmigung für eine bestimmte Adresse + Löst einen Fehler aus, wenn `_owner` nicht der aktuelle Besitzer ist. + """ + # Löst einen Fehler aus, wenn `_owner` nicht der aktuelle Besitzer ist + assert self.idToOwner[_tokenId] == _owner + if self.idToApprovals[_tokenId] != ZERO_ADDRESS: + # Genehmigungen zurücksetzen + self.idToApprovals[_tokenId] = ZERO_ADDRESS +``` + +Ändern Sie den Wert nur, wenn es nötig ist. Zustandsvariablen leben im Speicher. Das Schreiben in den Speicher ist +eine der teuersten Operationen, die die EVM (Ethereum Virtual Machine) durchführt (in Bezug auf +[Gas](/developers/docs/gas/)). Daher ist es eine gute Idee, dies zu minimieren, denn selbst das Schreiben des +vorhandenen Wertes hat hohe Kosten. + +```python +@internal +def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address): + """ + @dev Führt die Übertragung eines NFT aus. + Löst eine Ausnahme aus, es sei denn, `msg.sender` ist der aktuelle Eigentümer, ein autorisierter Betreiber oder die genehmigte + Adresse für diesen NFT. (HINWEIS: `msg.sender` ist in einer privaten Funktion nicht erlaubt, also `_sender` übergeben.) + Löst eine Ausnahme aus, wenn `_to` die Null-Adresse ist. + Löst eine Ausnahme aus, wenn `_from` nicht der aktuelle Eigentümer ist. + Löst eine Ausnahme aus, wenn `_tokenId` kein gültiger NFT ist. + """ +``` + +Wir haben diese interne Funktion, weil es zwei Möglichkeiten gibt, Token zu übertragen (regulär und sicher), aber +wir wollen nur eine einzige Stelle im Code, an der wir dies tun, um die Prüfung zu erleichtern. + +```python + # Anforderungen prüfen + assert self._isApprovedOrOwner(_sender, _tokenId) + # Löst einen Fehler aus, wenn `_to` die Null-Adresse ist + assert _to != ZERO_ADDRESS + # Genehmigung löschen. Löst einen Fehler aus, wenn `_from` nicht der aktuelle Besitzer ist + self._clearApproval(_from, _tokenId) + # NFT entfernen. Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist + self._removeTokenFrom(_from, _tokenId) + # NFT hinzufügen + self._addTokenTo(_to, _tokenId) + # Die Übertragung protokollieren + log Transfer(_from, _to, _tokenId) +``` + +Um ein Ereignis in Vyper auszulösen, verwenden Sie eine `log`-Anweisung ([siehe hier für weitere Details](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging)). + +#### Transfer-Funktionen {#transfer-funs} + +```python + +### TRANSFER-FUNKTIONEN ### + +@external +def transferFrom(_from: address, _to: address, _tokenId: uint256): + """ + @dev Löst einen Fehler aus, es sei denn `msg.sender` ist der aktuelle Eigentümer, ein autorisierter Betreiber oder die genehmigte + Adresse für diesen NFT. + Löst einen Fehler aus, wenn `_from` nicht der aktuelle Eigentümer ist. + Löst einen Fehler aus, wenn `_to` die Null-Adresse ist. + Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist. + @notice Der Aufrufer ist dafür verantwortlich zu bestätigen, dass `_to` in der Lage ist, NFTs zu empfangen, andernfalls + können sie dauerhaft verloren gehen. + @param _from Der aktuelle Eigentümer des NFT. + @param _to Der neue Eigentümer. + @param _tokenId Der zu übertragende NFT. + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +Mit dieser Funktion können Sie an eine beliebige Adresse übertragen. Sofern die Adresse nicht einem Benutzer oder einem Vertrag gehört, der +weiß, wie man Token überträgt, wird jeder Token, den Sie übertragen, an dieser Adresse hängen bleiben und unbrauchbar sein. + +```python +@external +def safeTransferFrom( + _from: address, + _to: address, + _tokenId: uint256, + _data: Bytes[1024]=b"" + ): + """ + @dev Überträgt das Eigentum an einem NFT von einer Adresse an eine andere. + Löst eine Ausnahme aus, es sei denn `msg.sender` ist der aktuelle Eigentümer, ein autorisierter Betreiber oder die + genehmigte Adresse für diesen NFT. + Löst eine Ausnahme aus, wenn `_from` nicht der aktuelle Eigentümer ist. + Löst eine Ausnahme aus, wenn `_to` die Null-Adresse ist. + Löst eine Ausnahme aus, wenn `_tokenId` kein gültiger NFT ist. + Wenn `_to` ein Smart Contract ist, ruft es `onERC721Received` auf `_to` auf und löst einen Fehler aus, wenn + der Rückgabewert nicht `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` ist. + HINWEIS: bytes4 wird durch bytes32 mit Padding dargestellt + @param _from Der aktuelle Eigentümer des NFT. + @param _to Der neue Eigentümer. + @param _tokenId Der zu übertragende NFT. + @param _data Zusätzliche Daten ohne spezifiziertes Format, die im Aufruf an `_to` gesendet werden. + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +Es ist in Ordnung, die Übertragung zuerst durchzuführen, denn wenn es ein Problem gibt, werden wir es sowieso rückgängig machen, +so dass alles, was in dem Aufruf getan wird, abgebrochen wird. + +```python + if _to.is_contract: # prüfen, ob `_to` eine Vertragsadresse ist +``` + +Prüfen Sie zunächst, ob die Adresse ein Vertrag ist (ob sie Code enthält). Wenn nicht, gehen Sie davon aus, dass es sich um eine Benutzeradresse +handelt und der Benutzer den Token verwenden oder übertragen kann. Aber lassen Sie sich nicht +in falscher Sicherheit wiegen. Sie können Token auch mit `safeTransferFrom` verlieren, wenn Sie sie +an eine Adresse übertragen, für die niemand den privaten Schlüssel kennt. + +```python + returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) +``` + +Rufen Sie den Zielvertrag auf, um zu sehen, ob er ERC-721-Token empfangen kann. + +```python + # Löst einen Fehler aus, wenn das Übertragungsziel ein Vertrag ist, der 'onERC721Received' nicht implementiert + assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32) +``` + +Wenn das Ziel ein Vertrag ist, der aber keine ERC-721-Token akzeptiert (oder der sich entschieden hat, diese +besondere Übertragung nicht zu akzeptieren), wird die Aktion rückgängig gemacht. + +```python +@external +def approve(_approved: address, _tokenId: uint256): + """ + @dev Legt die genehmigte Adresse für einen NFT fest oder bestätigt sie erneut. Die Null-Adresse gibt an, dass es keine genehmigte Adresse gibt. + Löst einen Fehler aus, es sei denn `msg.sender` ist der aktuelle NFT-Eigentümer oder ein autorisierter Betreiber des aktuellen Eigentümers. + Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist. (HINWEIS: Dies ist nicht im EIP geschrieben) + Löst einen Fehler aus, wenn `_approved` der aktuelle Eigentümer ist. (HINWEIS: Dies ist nicht im EIP geschrieben) + @param _approved Adresse, die für die angegebene NFT-ID genehmigt werden soll. + @param _tokenId ID des zu genehmigenden Tokens. + """ + owner: address = self.idToOwner[_tokenId] + # Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist + assert owner != ZERO_ADDRESS + # Löst einen Fehler aus, wenn `_approved` der aktuelle Eigentümer ist + assert _approved != owner +``` + +Wenn Sie konventionsgemäß keinen Genehmiger haben wollen, ernennen Sie die Null-Adresse, nicht sich selbst. + +```python + # Anforderungen prüfen + senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender + senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender] + assert (senderIsOwner or senderIsApprovedForAll) +``` + +Um eine Genehmigung zu erteilen, können Sie entweder der Eigentümer sein oder ein vom Eigentümer autorisierter Betreiber. + +```python + # Die Genehmigung festlegen + self.idToApprovals[_tokenId] = _approved + log Approval(owner, _approved, _tokenId) + + +@external +def setApprovalForAll(_operator: address, _approved: bool): + """ + @dev Aktiviert oder deaktiviert die Genehmigung für einen Dritten ("Betreiber"), alle + Vermögenswerte von `msg.sender` zu verwalten. Es löst auch das ApprovalForAll-Ereignis aus. + Löst einen Fehler aus, wenn `_operator` der `msg.sender` ist. (HINWEIS: Dies ist nicht im EIP geschrieben) + @notice Dies funktioniert auch, wenn der Absender zu diesem Zeitpunkt keine Token besitzt. + @param _operator Adresse, die zum Satz der autorisierten Betreiber hinzugefügt werden soll. + @param _approved True, wenn die Betreiber genehmigt sind, false, um die Genehmigung zu widerrufen. + """ + # Löst einen Fehler aus, wenn `_operator` der `msg.sender` ist + assert _operator != msg.sender + self.ownerToOperators[msg.sender][_operator] = _approved + log ApprovalForAll(msg.sender, _operator, _approved) +``` + +#### Neue Token prägen und bestehende zerstören {#mint-burn} + +Das Konto, das den Vertrag erstellt hat, ist der `minter`, der Superuser, der berechtigt ist, neue +NFTs zu prägen. Jedoch ist es ihm nicht erlaubt, bestehende Token zu verbrennen. Nur der Eigentümer oder eine vom Eigentümer +autorisierte Entität kann dies tun. + +```python +### PRÄGE- & VERBRENNUNGSFUNKTIONEN ### + +@external +def mint(_to: address, _tokenId: uint256) -> bool: +``` + +Diese Funktion gibt immer `True` zurück, denn wenn die Operation fehlschlägt, wird sie rückgängig gemacht. + +```python + """ + @dev Funktion zum Prägen von Token + Löst einen Fehler aus, wenn `msg.sender` nicht der Minter ist. + Löst einen Fehler aus, wenn `_to` die Null-Adresse ist. + Löst einen Fehler aus, wenn `_tokenId` jemandem gehört. + @param _to Die Adresse, die die geprägten Token erhalten wird. + @param _tokenId Die zu prägende Token-ID. + @return Ein boolescher Wert, der angibt, ob die Operation erfolgreich war. + """ + # Löst einen Fehler aus, wenn `msg.sender` nicht der Minter ist + assert msg.sender == self.minter +``` + +Nur der Minter (das Konto, das den ERC-721-Vertrag erstellt hat) kann neue Token prägen. Dies kann in der Zukunft ein +Problem sein, wenn wir die Identität des Minters ändern wollen. In +einem Produktionsvertrag würden Sie wahrscheinlich eine Funktion wollen, die es dem Minter erlaubt, Minter-Privilegien +an jemand anderen zu übertragen. + +```python + # Löst einen Fehler aus, wenn `_to` die Null-Adresse ist + assert _to != ZERO_ADDRESS + # NFT hinzufügen. Löst einen Fehler aus, wenn `_tokenId` jemandem gehört + self._addTokenTo(_to, _tokenId) + log Transfer(ZERO_ADDRESS, _to, _tokenId) + return True +``` + +Konventionsgemäß zählt das Prägen neuer Token als Übertragung von der Adresse Null. + +```python + +@external +def burn(_tokenId: uint256): + """ + @dev Verbrennt einen bestimmten ERC721-Token. + Löst einen Fehler aus, es sei denn `msg.sender` ist der aktuelle Eigentümer, ein autorisierter Betreiber oder die genehmigte + Adresse für diesen NFT. + Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist. + @param _tokenId uint256 ID des zu verbrennenden ERC721-Tokens. + """ + # Anforderungen prüfen + assert self._isApprovedOrOwner(msg.sender, _tokenId) + owner: address = self.idToOwner[_tokenId] + # Löst einen Fehler aus, wenn `_tokenId` kein gültiger NFT ist + assert owner != ZERO_ADDRESS + self._clearApproval(owner, _tokenId) + self._removeTokenFrom(owner, _tokenId) + log Transfer(owner, ZERO_ADDRESS, _tokenId) +``` + +Jeder, der berechtigt ist, einen Token zu übertragen, darf ihn auch verbrennen. Während ein Verbrennen einer Übertragung +an die Null-Adresse gleichkommt, empfängt die Null-Adresse den Token nicht tatsächlich. Dies ermöglicht es uns, +den gesamten Speicher freizugeben, der für den Token verwendet wurde, was die Gaskosten der Transaktion reduzieren kann. + +## Verwendung dieses Vertrags {#using-contract} + +Im Gegensatz zu Solidity hat Vyper keine Vererbung. Dies ist eine bewusste Designentscheidung, um den +Code klarer und damit leichter zu sichern. Um also Ihren eigenen Vyper ERC-721-Vertrag zu erstellen, nehmen Sie diesen +Vertrag und modifizieren Sie ihn, +um die gewünschte Geschäftslogik zu implementieren. + +## Fazit {#conclusion} + +Zur Wiederholung hier einige der wichtigsten Ideen in diesem Vertrag: + +- Um ERC-721-Token mit einer sicheren Übertragung zu empfangen, müssen Verträge die `ERC721Receiver`-Schnittstelle implementieren. +- Auch wenn Sie eine sichere Übertragung verwenden, können Token immer noch stecken bleiben, wenn Sie sie an eine Adresse senden, deren privater Schlüssel + unbekannt ist. +- Wenn es ein Problem mit einer Operation gibt, ist es eine gute Idee, den Aufruf `rückgängig zu machen`, anstatt nur + einen Fehlerwert zurückzugeben. +- ERC-721-Token existieren, wenn sie einen Besitzer haben. +- Es gibt drei Möglichkeiten, zur Übertragung eines NFT autorisiert zu sein. Sie können der Eigentümer sein, für einen bestimmten Token zugelassen sein + oder ein Betreiber für alle Token des Eigentümers sein. +- Vergangene Ereignisse sind nur außerhalb der Blockchain sichtbar. Code, der innerhalb der Blockchain ausgeführt wird, kann sie nicht anzeigen. + +Gehen Sie nun hin und implementieren Sie sichere Vyper-Verträge. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). + diff --git a/public/content/translations/de/developers/tutorials/erc20-annotated-code/index.md b/public/content/translations/de/developers/tutorials/erc20-annotated-code/index.md new file mode 100644 index 00000000000..df0c88271ed --- /dev/null +++ b/public/content/translations/de/developers/tutorials/erc20-annotated-code/index.md @@ -0,0 +1,930 @@ +--- +title: "ERC-20-Vertrag – exemplarische Vorgehensweise" +description: Was beinhaltet der OpenZeppelin ERC-20-Vertrag und warum ist er da? +author: Ori Pomerantz +lang: de +tags: [ "solidity", "Erc-20" ] +skill: beginner +published: 2021-03-09 +--- + +## Einführung {#introduction} + +Eine der häufigsten Anwendungen von Ethereum ist die Schaffung eines handelbaren Tokens durch eine Gruppe, der gewissermaßen ihre eigene Währung darstellt. Diese Token folgen typischerweise einem Standard, +[ERC-20](/developers/docs/standards/tokens/erc-20/). Dieser Standard ermöglicht es, Tools wie Liquiditätspools und Wallets zu entwickeln, die mit allen ERC-20- +Token funktionieren. In diesem Artikel analysieren wir die +[OpenZeppelin Solidity ERC20-Implementierung](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) sowie die +[Interface-Definition](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol). + +Dies ist kommentierter Quellcode. Wenn Sie ERC-20 implementieren möchten, +[lesen Sie dieses Tutorial](https://docs.openzeppelin.com/contracts/2.x/erc20-supply). + +## Die Schnittstelle {#the-interface} + +Der Zweck eines Standards wie ERC-20 besteht darin, eine Vielzahl von Token-Implementierungen zu ermöglichen, die mit anderen Anwendungen wie Wallets und dezentralen Börsen interoperabel sind. Um das zu erreichen, erstellen wir eine +[Schnittstelle](https://www.geeksforgeeks.org/solidity/solidity-basics-of-interface/). Jeder Code, der den Token-Vertrag verwenden muss, kann die gleichen Definitionen in der Schnittstelle verwenden und mit allen Token-Verträgen, die ihn verwenden, kompatibel sein, unabhängig davon, ob es sich um eine Wallet wie +MetaMask, eine Dapp wie etherscan.io oder einen anderen Vertrag wie einen Liquiditätspool handelt. + +![Illustration der ERC-20-Schnittstelle](erc20_interface.png) + +Wenn Sie ein erfahrener Programmierer sind, erinnern Sie sich wahrscheinlich an ähnliche Konstrukte in [Java](https://www.w3schools.com/java/java_interface.asp) +oder sogar in [C-Header-Dateien](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html). + +Dies ist eine Definition der [ERC-20-Schnittstelle](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) +von OpenZeppelin. Es ist eine Übersetzung des [für Menschen lesbaren Standards](https://eips.ethereum.org/EIPS/eip-20) in Solidity-Code. Natürlich definiert die +Schnittstelle selbst nicht, _wie_ etwas zu tun ist. Dies wird im nachstehenden Vertragsquelltext erläutert. + +  + +```solidity +// SPDX-License-Identifier: MIT +``` + +Solidity-Dateien sollten einen Lizenzbezeichner enthalten. [Die Liste der Lizenzen finden Sie hier](https://spdx.org/licenses/). Wenn Sie eine andere +Lizenz benötigen, erklären Sie dies einfach in den Kommentaren. + +  + +```solidity +pragma solidity >=0.6.0 <0.8.0; +``` + +Die Sprache Solidity entwickelt sich noch immer schnell weiter, und neue Versionen sind möglicherweise nicht mit altem Code kompatibel +([siehe hier](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html)). Daher ist es eine gute Idee, nicht nur eine Mindestversion +der Sprache anzugeben, sondern auch eine Höchstversion, die letzte, mit der Sie den Code getestet haben. + +  + +```solidity +/** + * @dev Schnittstelle des ERC20-Standards, wie im EIP definiert. + */ +``` + +Das `@dev` im Kommentar ist Teil des [NatSpec-Formats](https://docs.soliditylang.org/en/develop/natspec-format.html), das zur Erstellung von +Dokumentation aus dem Quellcode verwendet wird. + +  + +```solidity +interface IERC20 { +``` + +Konventionsgemäß beginnen Schnittstellennamen mit `I`. + +  + +```solidity + /** + * @dev Gibt die Menge der existierenden Token zurück. + */ + function totalSupply() external view returns (uint256); +``` + +Diese Funktion ist `external`, was bedeutet, dass [sie nur von außerhalb des Vertrags aufgerufen werden kann](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2). +Sie gibt den Gesamtvorrat an Token im Vertrag zurück. Dieser Wert wird mit dem in Ethereum gebräuchlichsten Typ zurückgegeben, 256 Bit ohne Vorzeichen (256 Bit ist die +native Wortgröße der EVM). Diese Funktion ist auch eine `view`, was bedeutet, dass sie den Zustand nicht ändert, so dass sie auf einem einzelnen Knoten ausgeführt werden kann, anstatt dass sie +auf jedem Knoten in der Blockchain ausgeführt werden muss. Diese Art von Funktion erzeugt keine Transaktion und kostet kein [Gas](/developers/docs/gas/). + +**Hinweis:** Theoretisch könnte es so aussehen, als ob der Ersteller eines Vertrags betrügen könnte, indem er einen geringeren Gesamtvorrat als den tatsächlichen Wert zurückgibt, wodurch jeder Token +wertvoller erscheint, als er tatsächlich ist. Diese Befürchtung ignoriert jedoch die wahre Natur der Blockchain. Alles, was auf der Blockchain geschieht, kann von +jedem Knoten verifiziert werden. Um dies zu erreichen, sind der Maschinencode und der Speicher jedes Vertrags auf jedem Knoten verfügbar. Obwohl Sie nicht verpflichtet sind, den Solidity-Code +für Ihren Vertrag zu veröffentlichen, würde Sie niemand ernst nehmen, wenn Sie nicht den Quellcode und die Version von Solidity, mit der er kompiliert wurde, veröffentlichen, damit er +mit dem von Ihnen bereitgestellten Maschinencode verifiziert werden kann. +Siehe zum Beispiel [diesen Vertrag](https://eth.blockscout.com/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD?tab=contract). + +  + +```solidity + /** + * @dev Gibt die Menge an Token zurück, die `account` besitzt. + */ + function balanceOf(address account) external view returns (uint256); +``` + +Wie der Name schon sagt, gibt `balanceOf` den Saldo eines Kontos zurück. Ethereum-Konten werden in Solidity mit dem Typ `address` identifiziert, der 160 Bit enthält. +Sie ist ebenfalls `external` und `view`. + +  + +```solidity + /** + * @dev Verschiebt `amount` Token vom Konto des Aufrufers an `recipient`. + * + * Gibt einen booleschen Wert zurück, der angibt, ob die Operation erfolgreich war. + * + * Löst ein {Transfer}-Ereignis aus. + */ + function transfer(address recipient, uint256 amount) external returns (bool); +``` + +Die Funktion `transfer` überträgt Token vom Aufrufer an eine andere Adresse. Dies beinhaltet eine Zustandsänderung, also ist es keine `view`. +Wenn ein Nutzer diese Funktion aufruft, erzeugt das eine Transaktion und kostet Gas. Sie löst auch ein Ereignis, `Transfer`, aus, um alle auf +der Blockchain über das Ereignis zu informieren. + +Die Funktion hat zwei Arten von Ausgaben für zwei verschiedene Arten von Aufrufern: + +- Benutzer, die die Funktion direkt über eine Benutzeroberfläche aufrufen. Typischerweise sendet der Nutzer eine Transaktion + und wartet nicht auf eine Antwort, was unbestimmt lange dauern kann. Der Nutzer kann sehen, was passiert ist, + indem er nach dem Transaktionsbeleg (der durch den Transaktions-Hash identifiziert wird) oder nach dem + `Transfer`-Ereignis sucht. +- Andere Verträge, die die Funktion als Teil einer Gesamttransaktion aufrufen. Diese Verträge erhalten das Ergebnis sofort, + da sie in derselben Transaktion laufen und somit den Rückgabewert der Funktion verwenden können. + +Die gleiche Art von Ausgabe wird von den anderen Funktionen erzeugt, die den Zustand des Vertrags ändern. + +  + +Berechtigungen („Allowances“) erlauben es einem Konto, einige Token auszugeben, die einem anderen Besitzer gehören. +Dies ist z. B. für Verträge nützlich, die als Verkäufer fungieren. Verträge können nicht +auf Ereignisse warten. Wenn also ein Käufer Token direkt an den Verkäufervertrag überweisen würde, +wüsste dieser Vertrag nicht, dass er bezahlt wurde. Stattdessen erlaubt der Käufer dem +Verkäufervertrag, einen bestimmten Betrag auszugeben, und der Verkäufer überweist diesen Betrag. +Dies geschieht über eine Funktion, die der Verkäufervertrag aufruft, sodass der Verkäufervertrag +wissen kann, ob sie erfolgreich war. + +```solidity + /** + * @dev Gibt die verbleibende Anzahl von Token zurück, die `spender` im + * Namen von `owner` über {transferFrom} ausgeben darf. Dieser Wert ist + * standardmäßig null. + * + * Dieser Wert ändert sich, wenn {approve} oder {transferFrom} aufgerufen werden. + */ + function allowance(address owner, address spender) external view returns (uint256); +``` + +Die Funktion `allowance` ermöglicht es jedem, abzufragen, welche Berechtigung eine +Adresse (`owner`) einer anderen Adresse (`spender`) zum Ausgeben erteilt. + +  + +```solidity + /** + * @dev Legt `amount` als die Berechtigung von `spender` über die Token des Aufrufers fest. + * + * Gibt einen booleschen Wert zurück, der angibt, ob die Operation erfolgreich war. + * + * WICHTIG: Beachten Sie, dass das Ändern einer Berechtigung mit dieser Methode das Risiko birgt, + * dass jemand durch eine unglückliche Transaktionsreihenfolge sowohl die alte als auch die neue + * Berechtigung verwenden kann. Eine mögliche Lösung zur Minderung dieser Race-Condition + * besteht darin, zuerst die Berechtigung des Spenders auf 0 zu reduzieren und danach den + * gewünschten Wert festzulegen: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Löst ein {Approval}-Ereignis aus. + */ + function approve(address spender, uint256 amount) external returns (bool); +``` + +Die Funktion `approve` erstellt eine Berechtigung. Lesen Sie unbedingt die Meldung darüber, +wie sie missbraucht werden kann. In Ethereum kontrollieren Sie die Reihenfolge Ihrer eigenen Transaktionen, +aber Sie können nicht die Reihenfolge kontrollieren, in der die Transaktionen anderer Personen ausgeführt werden, +es sei denn, Sie reichen Ihre eigene Transaktion erst ein, wenn Sie sehen, dass +die Transaktion der anderen Seite stattgefunden hat. + +  + +```solidity + /** + * @dev Verschiebt `amount` Token von `sender` zu `recipient` unter Verwendung des + * Berechtigungsmechanismus. `amount` wird dann von der Berechtigung des Aufrufers + * abgezogen. + * + * Gibt einen booleschen Wert zurück, der angibt, ob die Operation erfolgreich war. + * + * Löst ein {Transfer}-Ereignis aus. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); +``` + +Schließlich wird `transferFrom` vom Ausgebenden verwendet, um die Berechtigung tatsächlich zu nutzen. + +  + +```solidity + + /** + * @dev Wird ausgelöst, wenn `value` Token von einem Konto (`from`) auf + * ein anderes (`to`) verschoben werden. + * + * Beachten Sie, dass `value` null sein kann. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Wird ausgelöst, wenn die Berechtigung eines `spender` für einen `owner` durch + * einen Aufruf von {approve} festgelegt wird. `value` ist die neue Berechtigung. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} +``` + +Diese Ereignisse werden ausgelöst, wenn sich der Zustand des ERC-20-Vertrags ändert. + +## Der eigentliche Vertrag {#the-actual-contract} + +Dies ist der eigentliche Vertrag, der den ERC-20-Standard implementiert, +[entnommen von hier](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Er ist nicht dafür gedacht, so wie er ist verwendet zu werden, aber Sie können +[davon erben](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm), um ihn zu etwas Nutzbarem zu erweitern. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.8.0; +``` + +  + +### Import-Anweisungen {#import-statements} + +Zusätzlich zu den oben genannten Interface-Definitionen importiert die Contract-Definition zwei weitere Dateien: + +```solidity + +import "../../GSN/Context.sol"; +import "./IERC20.sol"; +import "../../math/SafeMath.sol"; +``` + +- `GSN/Context.sol` enthält die Definitionen, die für die Verwendung von [OpenGSN](https://www.opengsn.org/) erforderlich sind, einem System, das es Nutzern ohne Ether + ermöglicht, die Blockchain zu verwenden. Beachten Sie, dass dies eine alte Version ist. Wenn Sie OpenGSN integrieren möchten, + [verwenden Sie dieses Tutorial](https://docs.opengsn.org/javascript-client/tutorial.html). +- [Die SafeMath-Bibliothek](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/), die + arithmetische Über- und Unterläufe für Solidity-Versionen **<0.8.0** verhindert. In Solidity ≥0.8.0 werden arithmetische Operationen bei + Über-/Unterlauf automatisch zurückgesetzt, wodurch SafeMath überflüssig wird. Dieser Vertrag verwendet SafeMath für die Abwärtskompatibilität mit + älteren Compiler-Versionen. + +  + +Dieser Kommentar erklärt den Zweck des Vertrags. + +```solidity +/** + * @dev Implementierung der {IERC20}-Schnittstelle. + * + * Diese Implementierung ist agnostisch gegenüber der Art und Weise, wie Token erstellt werden. Das bedeutet, + * dass ein Versorgungsmechanismus in einem abgeleiteten Vertrag mit {_mint} hinzugefügt werden muss. + * Einen generischen Mechanismus finden Sie unter {ERC20PresetMinterPauser}. + * + * TIPP: Eine detaillierte Beschreibung finden Sie in unserem Leitfaden + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[Wie + * Versorgungsmechanismen implementiert werden]. + * + * Wir haben die allgemeinen OpenZeppelin-Richtlinien befolgt: Funktionen werden zurückgesetzt, anstatt + * bei einem Fehler `false` zurückzugeben. Dieses Verhalten ist jedoch konventionell + * und steht nicht im Widerspruch zu den Erwartungen von ERC20-Anwendungen. + * + * Zusätzlich wird bei Aufrufen von {transferFrom} ein {Approval}-Ereignis ausgelöst. + * Dies ermöglicht es Anwendungen, die Berechtigung für alle Konten allein + * durch das Abhören dieser Ereignisse zu rekonstruieren. Andere Implementierungen des EIP geben + * diese Ereignisse möglicherweise nicht aus, da dies nicht von der Spezifikation gefordert wird. + * + * Schließlich wurden die nicht standardmäßigen Funktionen {decreaseAllowance} und {increaseAllowance} + * hinzugefügt, um die bekannten Probleme bei der Festlegung von + * Berechtigungen zu entschärfen. Siehe {IERC20-approve}. + */ + +``` + +### Vertragsdefinition {#contract-definition} + +```solidity +contract ERC20 is Context, IERC20 { +``` + +Diese Zeile gibt die Vererbung an, in diesem Fall von `IERC20` von oben und `Context` für OpenGSN. + +  + +```solidity + + using SafeMath for uint256; + +``` + +Diese Zeile hängt die `SafeMath`-Bibliothek an den Typ `uint256` an. Sie können diese Bibliothek +[hier](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol) finden. + +### Variablendefinitionen {#variable-definitions} + +Diese Definitionen legen die Zustandsvariablen des Vertrags fest. Diese Variablen sind `private` deklariert, aber +das bedeutet nur, dass andere Verträge auf der Blockchain sie nicht lesen können. _Es gibt keine +Geheimnisse auf der Blockchain_, die Software auf jedem Knoten hat den Zustand jedes Vertrags +bei jedem Block. Konventionsgemäß werden Zustandsvariablen `_` genannt. + +Die ersten beiden Variablen sind [Mappings](https://www.tutorialspoint.com/solidity/solidity_mappings.htm), +was bedeutet, dass sie sich ungefähr wie [assoziative Arrays](https://wikipedia.org/wiki/Associative_array) verhalten, +außer dass die Schlüssel numerische Werte sind. Speicher wird nur für Einträge zugewiesen, die Werte haben, die sich vom +Standardwert (Null) unterscheiden. + +```solidity + mapping (address => uint256) private _balances; +``` + +Das erste Mapping, `_balances`, enthält Adressen und ihre jeweiligen Saldi dieses Tokens. Um auf +den Saldo zuzugreifen, verwenden Sie diese Syntax: `_balances[
]`. + +  + +```solidity + mapping (address => mapping (address => uint256)) private _allowances; +``` + +Diese Variable, `_allowances`, speichert die zuvor erläuterten Berechtigungen. Der erste Index ist der Besitzer +der Token, und der zweite ist der Vertrag mit der Berechtigung. Um auf den Betrag zuzugreifen, den Adresse A vom Konto von Adresse B +ausgeben kann, verwenden Sie `_allowances[B][A]`. + +  + +```solidity + uint256 private _totalSupply; +``` + +Wie der Name schon sagt, hält diese Variable den Gesamtvorrat an Token nach. + +  + +```solidity + string private _name; + string private _symbol; + uint8 private _decimals; +``` + +Diese drei Variablen werden verwendet, um die Lesbarkeit zu verbessern. Die ersten beiden sind selbsterklärend, aber `_decimals` +ist es nicht. + +Einerseits gibt es bei Ethereum keine Gleitkomma- oder Bruchvariablen. Andererseits +mögen es Menschen, Token teilen zu können. Ein Grund, warum man sich auf Gold als Währung einigte, war, +dass es schwer war, Wechselgeld herauszugeben, wenn jemand eine Ente im Wert einer Kuh kaufen wollte. + +Die Lösung besteht darin, ganze Zahlen zu verfolgen, aber anstelle des echten Tokens einen Bruchteil eines Tokens zu zählen, der +fast wertlos ist. Im Fall von Ether wird der Bruchteil des Tokens „Wei“ genannt, und 10^18 Wei entsprechen einem +ETH. Zum Zeitpunkt der Erstellung dieses Artikels entsprechen 10.000.000.000.000 Wei ungefähr einem US- oder Euro-Cent. + +Anwendungen müssen wissen, wie der Token-Saldo angezeigt wird. Wenn ein Nutzer 3.141.000.000.000.000.000 Wei hat, sind das +3,14 ETH? 31,41 ETH? 3.141 ETH? Im Fall von Ether ist 10^18 Wei zu ETH definiert, aber für Ihren +Token können Sie einen anderen Wert wählen. Wenn die Teilung des Tokens keinen Sinn macht, können Sie einen +`_decimals`-Wert von Null verwenden. Wenn Sie denselben Standard wie ETH verwenden möchten, verwenden Sie den Wert **18**. + +### Der Konstruktor {#the-constructor} + +```solidity + /** + * @dev Setzt die Werte für {name} und {symbol}, initialisiert {decimals} mit + * einem Standardwert von 18. + * + * Um einen anderen Wert für {decimals} zu wählen, verwenden Sie {_setupDecimals}. + * + * Alle drei dieser Werte sind unveränderlich: Sie können nur einmal während + * der Konstruktion festgelegt werden. + */ + constructor (string memory name_, string memory symbol_) public { + // In Solidity ≥0.7.0 ist 'public' implizit und kann weggelassen werden. + + _name = name_; + _symbol = symbol_; + _decimals = 18; + } +``` + +Der Konstruktor wird aufgerufen, wenn der Vertrag zum ersten Mal erstellt wird. Konventionsgemäß werden Funktionsparameter `_` genannt. + +### Benutzeroberflächenfunktionen {#user-interface-functions} + +```solidity + /** + * @dev Gibt den Namen des Tokens zurück. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Gibt das Symbol des Tokens zurück, normalerweise eine kürzere Version des + * Namens. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Gibt die Anzahl der Dezimalstellen zurück, die zur Darstellung für den Benutzer verwendet werden. + * Wenn `decimals` beispielsweise `2` ist, sollte ein Saldo von `505` Token + * einem Benutzer als `5,05` (`505 / 10 ** 2`) angezeigt werden. + * + * Token entscheiden sich normalerweise für einen Wert von 18 und ahmen damit die Beziehung zwischen + * Ether und Wei nach. Dies ist der Wert, den {ERC20} verwendet, es sei denn, {_setupDecimals} wird + * aufgerufen. + * + * HINWEIS: Diese Information wird nur zu _Anzeige_-Zwecken verwendet: Sie beeinflusst + * in keiner Weise die Arithmetik des Vertrags, einschließlich + * {IERC20-balanceOf} und {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } +``` + +Diese Funktionen, `name`, `symbol` und `decimals`, helfen Benutzeroberflächen, Ihren Vertrag zu kennen, damit sie ihn richtig anzeigen können. + +Der Rückgabetyp ist `string memory`, was bedeutet, dass eine Zeichenfolge zurückgegeben wird, die im Speicher gespeichert ist. Variablen, wie z. B. +Zeichenketten, können an drei Stellen gespeichert werden: + +| | Lebensdauer | Vertragszugriff | Gaskosten | +| ----------- | ---------------- | --------------- | ---------------------------------------------------------------------------------- | +| Speicher | Funktionsaufruf | Lesen/Schreiben | Zehner oder Hunderter (höher für höhere Lagen) | +| Aufrufdaten | Funktionsaufruf | Nur Lesen | Kann nicht als Rückgabetyp verwendet werden, sondern nur als Funktionsparametertyp | +| Speicherort | Bis zur Änderung | Lesen/Schreiben | Hoch (800 für Lesen, 20.000 für Schreiben) | + +In diesem Fall ist der Speicher die beste Wahl. + +### Token-Informationen lesen {#read-token-information} + +Dies sind Funktionen, die Informationen über den Token liefern, entweder den Gesamtvorrat oder den +Saldo eines Kontos. + +```solidity + /** + * @dev Siehe {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } +``` + +Die Funktion `totalSupply` gibt den Gesamtvorrat an Token zurück. + +  + +```solidity + /** + * @dev Siehe {IERC20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } +``` + +Lesen Sie den Saldo eines Kontos. Beachten Sie, dass jeder den Kontostand eines anderen abrufen darf. Es hat keinen Sinn, diese Informationen zu verbergen, da sie ohnehin auf jedem Knoten verfügbar sind. _Es gibt keine Geheimnisse in der Blockchain._ + +### Token übertragen {#transfer-tokens} + +```solidity + /** + * @dev Siehe {IERC20-transfer}. + * + * Anforderungen: + * + * - `recipient` darf nicht die Null-Adresse sein. + * - Der Aufrufer muss einen Saldo von mindestens `amount` haben. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { +``` + +Die Funktion `transfer` wird aufgerufen, um Token vom Konto des Absenders auf ein anderes zu übertragen. Beachten Sie, +dass sie zwar einen booleschen Wert zurückgibt, dieser aber immer **true** ist. Wenn die Übertragung +fehlschlägt, setzt der Vertrag den Aufruf zurück. + +  + +```solidity + _transfer(_msgSender(), recipient, amount); + return true; + } +``` + +Die Funktion `_transfer` erledigt die eigentliche Arbeit. Es handelt sich um eine private Funktion, die nur von +anderen Vertragsfunktionen aufgerufen werden kann. Konventionsgemäß werden private Funktionen wie Zustands- +variablen `_` genannt. + +Normalerweise verwenden wir in Solidity `msg.sender` für den Absender der Nachricht. Das stört jedoch +[OpenGSN](http://opengsn.org/). Wenn wir Transaktionen ohne Ether mit unserem Token zulassen wollen, müssen wir +`_msgSender()` verwenden. Sie gibt `msg.sender` für normale Transaktionen zurück, aber für solche ohne Ether +gibt sie den ursprünglichen Unterzeichner und nicht den Vertrag zurück, der die Nachricht weitergeleitet hat. + +### Berechtigungsfunktionen {#allowance-functions} + +Dies sind die Funktionen, die die Berechtigungsfunktionalität implementieren: `allowance`, `approve`, `transferFrom` +und `_approve`. Zusätzlich geht die OpenZeppelin-Implementierung über den Basisstandard hinaus und enthält einige Funktionen, die die +Sicherheit verbessern: `increaseAllowance` und `decreaseAllowance`. + +#### Die allowance-Funktion {#allowance} + +```solidity + /** + * @dev Siehe {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } +``` + +Die Funktion `allowance` ermöglicht es jedem, jede Berechtigung zu überprüfen. + +#### Die approve-Funktion {#approve} + +```solidity + /** + * @dev Siehe {IERC20-approve}. + * + * Anforderungen: + * + * - `spender` darf nicht die Null-Adresse sein. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { +``` + +Diese Funktion wird aufgerufen, um eine Berechtigung zu erstellen. Sie ähnelt der obigen `transfer`-Funktion: + +- Die Funktion ruft lediglich eine interne Funktion auf (in diesem Fall `_approve`), die die eigentliche Arbeit erledigt. +- Die Funktion gibt entweder `true` zurück (wenn erfolgreich) oder wird zurückgesetzt (wenn nicht). + +  + +```solidity + _approve(_msgSender(), spender, amount); + return true; + } +``` + +Wir verwenden interne Funktionen, um die Anzahl der Stellen, an denen Zustandsänderungen stattfinden, zu minimieren. _Jede_ Funktion, die den +Zustand ändert, stellt ein potenzielles Sicherheitsrisiko dar, das auf Sicherheit geprüft werden muss. Auf diese Weise ist die Wahrscheinlichkeit geringer, dass wir etwas falsch machen. + +#### Die transferFrom-Funktion {#transferFrom} + +Dies ist die Funktion, die ein Ausgebender aufruft, um eine Berechtigung zu nutzen. Dies erfordert zwei Operationen: die Übertragung des ausgegebenen +Betrags und die Reduzierung der Berechtigung um diesen Betrag. + +```solidity + /** + * @dev Siehe {IERC20-transferFrom}. + * + * Löst ein {Approval}-Ereignis aus, das die aktualisierte Berechtigung anzeigt. Dies ist nicht + * vom EIP gefordert. Siehe den Hinweis am Anfang von {ERC20}. + * + * Anforderungen: + * + * - `sender` und `recipient` dürfen nicht die Null-Adresse sein. + * - `sender` muss einen Saldo von mindestens `amount` haben. + * - der Aufrufer muss eine Berechtigung für die Token von `sender` von mindestens + * `amount` haben. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual + override returns (bool) { + _transfer(sender, recipient, amount); +``` + +  + +Der Funktionsaufruf `a.sub(b, "message")` tut zwei Dinge. Erstens berechnet er `a-b`, was die neue Berechtigung ist. +Zweitens prüft er, ob dieses Ergebnis nicht negativ ist. Wenn es negativ ist, wird der Aufruf mit der angegebenen Nachricht zurückgesetzt. Beachten Sie, dass bei einem Zurücksetzen eines Aufrufs jede zuvor während dieses Aufrufs durchgeführte Verarbeitung ignoriert wird, sodass wir den +`_transfer` nicht rückgängig machen müssen. + +```solidity + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, + "ERC20: transfer amount exceeds allowance")); + return true; + } +``` + +#### Sicherheitserweiterungen von OpenZeppelin {#openzeppelin-safety-additions} + +Es ist gefährlich, eine Berechtigung, die nicht Null ist, auf einen anderen Wert ungleich Null zu setzen, +da Sie nur die Reihenfolge Ihrer eigenen Transaktionen kontrollieren, nicht die von jemand anderem. Stellen Sie sich vor, +Sie haben zwei Nutzer, Alice, die naiv ist, und Bill, der unehrlich ist. Alice möchte eine Dienstleistung von +Bill, von der sie glaubt, dass sie fünf Token kostet – also gibt sie Bill eine Berechtigung von fünf Token. + +Dann ändert sich etwas und Bills Preis steigt auf zehn Token. Alice, die den Dienst immer noch will, +sendet eine Transaktion, die Bills Berechtigung auf zehn setzt. Sobald Bill diese neue Transaktion +im Transaktionspool sieht, sendet er eine Transaktion, die Alices fünf Token ausgibt und einen viel +höheren Gaspreis hat, damit sie schneller gemint wird. Auf diese Weise kann Bill zuerst fünf Token ausgeben und dann, +sobald Alices neue Berechtigung gemint wurde, zehn weitere ausgeben, für einen Gesamtpreis von fünfzehn Token, mehr als +Alice autorisieren wollte. Diese Technik wird +[Front-Running](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running) genannt + +| Alice-Transaktion | Alice-Nonce | Bill-Transaktion | Bill-Nonce | Bills Berechtigung | Bills Gesamteinkommen von 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 | + +Um dieses Problem zu vermeiden, ermöglichen es diese beiden Funktionen (`increaseAllowance` und `decreaseAllowance`), die +Berechtigung um einen bestimmten Betrag zu ändern. Wenn Bill also bereits fünf Token ausgegeben hat, wird er nur +noch fünf weitere ausgeben können. Je nach Zeitplan gibt es zwei Möglichkeiten, wie dies funktionieren kann, die beide damit enden, dass Bill nur zehn Token erhält: + +A: + +| Alice-Transaktion | Alice-Nonce | Bill-Transaktion | Bill-Nonce | Bills Berechtigung | Bills Gesamteinkommen von 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: + +| Alice-Transaktion | Alice-Nonce | Bill-Transaktion | Bill-Nonce | Bills Berechtigung | Bills Gesamteinkommen von 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 Erhöht atomar die dem `spender` vom Aufrufer gewährte Berechtigung. + * + * Dies ist eine Alternative zu {approve}, die als Milderung für + * in {IERC20-approve} beschriebene Probleme verwendet werden kann. + * + * Löst ein {Approval}-Ereignis aus, das die aktualisierte Berechtigung anzeigt. + * + * Anforderungen: + * + * - `spender` darf nicht die Null-Adresse sein. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } +``` + +Die Funktion `a.add(b)` ist eine sichere Addition. In dem unwahrscheinlichen Fall, dass `a`+`b`>=`2^256` ist, findet kein Umbruch +statt, wie es bei der normalen Addition der Fall ist. + +```solidity + + /** + * @dev Verringert atomar die dem `spender` vom Aufrufer gewährte Berechtigung. + * + * Dies ist eine Alternative zu {approve}, die als Milderung für + * in {IERC20-approve} beschriebene Probleme verwendet werden kann. + * + * Löst ein {Approval}-Ereignis aus, das die aktualisierte Berechtigung anzeigt. + * + * Anforderungen: + * + * - `spender` darf nicht die Null-Adresse sein. + * - `spender` muss eine Berechtigung für den Aufrufer von mindestens + * `subtractedValue` haben. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, + "ERC20: decreased allowance below zero")); + return true; + } +``` + +### Funktionen, die Token-Informationen ändern {#functions-that-modify-token-information} + +Dies sind die vier Funktionen, die die eigentliche Arbeit erledigen: `_transfer`, `_mint`, `_burn` und `_approve`. + +#### Die _transfer-Funktion {#_transfer} + +```solidity + /** + * @dev Verschiebt `amount` Token von `sender` zu `recipient`. + * + * Diese interne Funktion ist äquivalent zu {transfer} und kann verwendet werden, + * um z. B. automatische Token-Gebühren, Slashing-Mechanismen usw. zu implementieren. + * + * Löst ein {Transfer}-Ereignis aus. + * + * Anforderungen: + * + * - `sender` darf nicht die Null-Adresse sein. + * - `recipient` darf nicht die Null-Adresse sein. + * - `sender` muss einen Saldo von mindestens `amount` haben. + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual { +``` + +Diese Funktion, `_transfer`, überträgt Token von einem Konto auf ein anderes. Sie wird sowohl von +`transfer` (für Übertragungen vom eigenen Konto des Absenders) als auch von `transferFrom` (zur Verwendung von Berechtigungen +zur Übertragung vom Konto eines anderen) aufgerufen. + +  + +```solidity + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); +``` + +Niemand besitzt tatsächlich die Adresse Null in Ethereum (d. h., niemand kennt einen privaten Schlüssel, dessen zugehöriger öffentlicher Schlüssel +in die Null-Adresse umgewandelt wird). Wenn Leute diese Adresse verwenden, handelt es sich normalerweise um einen Softwarefehler – daher +schlagen wir fehl, wenn die Null-Adresse als Absender oder Empfänger verwendet wird. + +  + +```solidity + _beforeTokenTransfer(sender, recipient, amount); + +``` + +Es gibt zwei Möglichkeiten, diesen Vertrag zu verwenden: + +1. Verwenden Sie ihn als Vorlage für Ihren eigenen Code +2. [Erben Sie davon](https://www.bitdegree.org/learn/solidity-inheritance) und überschreiben Sie nur die Funktionen, die Sie ändern müssen + +Die zweite Methode ist viel besser, da der OpenZeppelin ERC-20-Code bereits geprüft wurde und als sicher gilt. Wenn Sie Vererbung verwenden, +ist es klar, welche Funktionen Sie ändern, und um Ihrem Vertrag zu vertrauen, müssen die Leute nur diese spezifischen Funktionen prüfen. + +Es ist oft nützlich, eine Funktion jedes Mal auszuführen, wenn Token den Besitzer wechseln. `_transfer` ist jedoch eine sehr wichtige Funktion und es ist +möglich, sie unsicher zu schreiben (siehe unten), daher ist es am besten, sie nicht zu überschreiben. Die Lösung ist `_beforeTokenTransfer`, eine +[Hook-Funktion](https://wikipedia.org/wiki/Hooking). Sie können diese Funktion überschreiben, und sie wird bei jeder Übertragung aufgerufen. + +  + +```solidity + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); +``` + +Dies sind die Zeilen, die die Übertragung tatsächlich durchführen. Beachten Sie, dass **nichts** zwischen ihnen steht und dass wir den +übertragenen Betrag vom Absender abziehen, bevor wir ihn dem Empfänger hinzufügen. Dies ist wichtig, denn wenn es in der Mitte einen +Aufruf an einen anderen Vertrag gäbe, hätte dieser genutzt werden können, um diesen Vertrag zu betrügen. Auf diese Weise ist die Übertragung +atomar, nichts kann in der Mitte davon passieren. + +  + +```solidity + emit Transfer(sender, recipient, amount); + } +``` + +Schließlich wird ein `Transfer`-Ereignis ausgelöst. Ereignisse sind für Smart Contracts nicht zugänglich, aber Code, der außerhalb der Blockchain +ausgeführt wird, kann auf Ereignisse lauschen und darauf reagieren. Zum Beispiel kann eine Wallet nachverfolgen, wann der Besitzer mehr Token erhält. + +#### Die _mint- und _burn-Funktionen {#_mint-and-_burn} + +Diese beiden Funktionen (`_mint` und `_burn`) ändern den Gesamtvorrat an Token. +Sie sind intern und es gibt keine Funktion, die sie in diesem Vertrag aufruft, +sie sind also nur nützlich, wenn Sie von dem Vertrag erben und Ihre eigene +Logik hinzufügen, um zu entscheiden, unter welchen Bedingungen neue Token gemint oder bestehende +gebrannt werden. + +**HINWEIS:** Jeder ERC-20-Token hat seine eigene Geschäftslogik, die die Token-Verwaltung vorschreibt. +Ein Vertrag mit festem Angebot könnte beispielsweise nur `_mint` +im Konstruktor aufrufen und niemals `_burn`. Ein Vertrag, der Token verkauft, +ruft `_mint` auf, wenn er bezahlt wird, und ruft vermutlich irgendwann `_burn` auf, +um eine galoppierende Inflation zu vermeiden. + +```solidity + /** @dev Erstellt `amount` Token und weist sie `account` zu, wodurch der + * Gesamtvorrat erhöht wird. + * + * Löst ein {Transfer}-Ereignis aus, bei dem `from` auf die Null-Adresse gesetzt ist. + * + * Anforderungen: + * + * - `to` darf nicht die Null-Adresse sein. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + _beforeTokenTransfer(address(0), account, amount); + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +Stellen Sie sicher, dass `_totalSupply` aktualisiert wird, wenn sich die Gesamtzahl der Token ändert. + +  + +```solidity + /** + * @dev Zerstört `amount` Token von `account` und reduziert so den + * Gesamtvorrat. + * + * Löst ein {Transfer}-Ereignis aus, bei dem `to` auf die Null-Adresse gesetzt ist. + * + * Anforderungen: + * + * - `account` darf nicht die Null-Adresse sein. + * - `account` muss mindestens `amount` Token besitzen. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } +``` + +Die Funktion `_burn` ist fast identisch mit `_mint`, außer dass sie in die andere Richtung geht. + +#### Die _approve-Funktion {#_approve} + +Dies ist die Funktion, die tatsächlich Berechtigungen festlegt. Beachten Sie, dass es einem Besitzer erlaubt, eine +Berechtigung anzugeben, die höher ist als der aktuelle Saldo des Besitzers. Das ist in Ordnung, da der Saldo zum Zeitpunkt der +Übertragung überprüft wird, wo er sich von dem Saldo unterscheiden kann, der bei der Erstellung der Berechtigung +vorhanden war. + +```solidity + /** + * @dev Legt `amount` als die Berechtigung von `spender` über die Token des `owner` fest. + * + * Diese interne Funktion ist äquivalent zu `approve` und kann verwendet werden, + * um z. B. automatische Berechtigungen für bestimmte Subsysteme festzulegen. + * + * Löst ein {Approval}-Ereignis aus. + * + * Anforderungen: + * + * - `owner` darf nicht die Null-Adresse sein. + * - `spender` darf nicht die Null-Adresse sein. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; +``` + +  + +Lösen Sie ein `Approval`-Ereignis aus. Je nachdem, wie die Anwendung geschrieben ist, kann der Vertrag des Ausgebenden über die +Genehmigung entweder vom Eigentümer oder von einem Server, der auf diese Ereignisse lauscht, informiert werden. + +```solidity + emit Approval(owner, spender, amount); + } + +``` + +### Die Dezimalvariable ändern {#modify-the-decimals-variable} + +```solidity + + + /** + * @dev Setzt {decimals} auf einen anderen Wert als den Standardwert 18. + * + * WARNUNG: Diese Funktion sollte nur vom Konstruktor aufgerufen werden. Die meisten + * Anwendungen, die mit Token-Verträgen interagieren, erwarten nicht, + * dass sich {decimals} jemals ändert, und könnten bei einer Änderung falsch funktionieren. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } +``` + +Diese Funktion ändert die Variable `_decimals`, die verwendet wird, um Benutzeroberflächen mitzuteilen, wie der Betrag zu interpretieren ist. +Sie sollten sie vom Konstruktor aus aufrufen. Es wäre unehrlich, sie zu einem späteren Zeitpunkt aufzurufen, und Anwendungen +sind nicht darauf ausgelegt, damit umzugehen. + +### Hooks {#hooks} + +```solidity + + /** + * @dev Hook, der vor jeder Übertragung von Token aufgerufen wird. Dies schließt + * Minting und Burning ein. + * + * Aufrufbedingungen: + * + * - Wenn `from` und `to` beide nicht null sind, wird `amount` der Token von `from` + * an `to` übertragen. + * - Wenn `from` null ist, werden `amount` Token für `to` gemint. + * - Wenn `to` null ist, wird `amount` der Token von `from` verbrannt. + * - `from` und `to` sind niemals beide null. + * + * Um mehr über Hooks zu erfahren, gehen Sie zu xref:ROOT:extending-contracts.adoc#using-hooks[Verwendung von Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} +``` + +Dies ist die Hook-Funktion, die bei Übertragungen aufgerufen wird. Sie ist hier leer, aber wenn Sie möchten, +dass sie etwas tut, überschreiben Sie sie einfach. + +## Fazit {#conclusion} + +Zur Wiederholung, hier sind einige der wichtigsten Ideen in diesem Vertrag (meiner Meinung nach, Ihre kann abweichen): + +- _Es gibt keine Geheimnisse in der Blockchain_. Alle Informationen, auf die ein Smart Contract zugreifen kann, + sind der ganzen Welt zugänglich. +- Sie können die Reihenfolge Ihrer eigenen Transaktionen kontrollieren, aber nicht, wann die Transaktionen anderer Personen + stattfinden. Dies ist der Grund, warum das Ändern einer Berechtigung gefährlich sein kann, da es dem + Ausgebenden ermöglicht, die Summe beider Berechtigungen auszugeben. +- Werte vom Typ `uint256` haben einen Überlauf. Mit anderen Worten: _0-1=2^256-1_. Wenn das kein erwünschtes + Verhalten ist, müssen Sie es überprüfen (oder die SafeMath-Bibliothek verwenden, die das für Sie tut). Beachten Sie, dass sich dies in + [Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html) geändert hat. +- Führen Sie alle Zustandsänderungen eines bestimmten Typs an einem bestimmten Ort durch, da dies die Prüfung erleichtert. + Dies ist der Grund, warum wir zum Beispiel `_approve` haben, das von `approve`, `transferFrom`, + `increaseAllowance` und `decreaseAllowance` aufgerufen wird +- Zustandsänderungen sollten atomar sein, ohne dass eine andere Aktion in ihrer Mitte stattfindet (wie Sie + in `_transfer` sehen können). Dies liegt daran, dass Sie während der Zustandsänderung einen inkonsistenten Zustand haben. Zum Beispiel + gibt es zwischen dem Zeitpunkt, an dem Sie vom Saldo des Absenders abziehen, und dem Zeitpunkt, an dem Sie dem Saldo des + Empfängers hinzufügen, weniger Token, als es sein sollten. Dies könnte potenziell missbraucht werden, wenn + Operationen zwischen ihnen stattfinden, insbesondere Aufrufe an einen anderen Vertrag. + +Jetzt, da Sie gesehen haben, wie der OpenZeppelin ERC-20-Vertrag geschrieben ist und insbesondere, wie er +sicherer gemacht wurde, können Sie Ihre eigenen sicheren Verträge und Anwendungen schreiben. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/erc20-with-safety-rails/index.md b/public/content/translations/de/developers/tutorials/erc20-with-safety-rails/index.md new file mode 100644 index 00000000000..cf7d6e742e6 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/erc20-with-safety-rails/index.md @@ -0,0 +1,217 @@ +--- +title: ERC-20 mit Sicherheitsvorkehrungen +description: Wie man Leuten helfen kann, dumme Fehler zu vermeiden +author: Ori Pomerantz +lang: de +tags: [ "Erc-20" ] +skill: beginner +published: 2022-08-15 +--- + +## Einführung {#introduction} + +Einer der großen Vorteile von Ethereum ist, dass es keine zentrale Instanz gibt, die Ihre Transaktionen ändern oder rückgängig machen kann. Eines der großen Probleme bei Ethereum ist, dass es keine zentrale Instanz gibt, die die Macht hat, Benutzerfehler oder illegale Transaktionen rückgängig zu machen. In diesem Artikel erfahren Sie mehr über einige der häufigsten Fehler, die Benutzer mit [ERC-20](/developers/docs/standards/tokens/erc-20/)-Token machen, sowie darüber, wie Sie ERC-20-Verträge erstellen, die den Benutzern helfen, diese Fehler zu vermeiden, oder die einer zentralen Instanz eine gewisse Macht geben (zum Beispiel das Einfrieren von Konten). + +Beachten Sie, dass wir zwar den [OpenZeppelin ERC-20-Token-Vertrag](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20) verwenden werden, dieser Artikel ihn aber nicht im Detail erklärt. Sie können diese Informationen [hier](/developers/tutorials/erc20-annotated-code) finden. + +Wenn Sie den vollständigen Quellcode sehen möchten: + +1. Öffnen Sie die [Remix IDE](https://remix.ethereum.org/). +2. Klicken Sie auf das GitHub-Klon-Symbol (![clone github icon](icon-clone.png)). +3. Klonen Sie das GitHub-Repository `https://github.com/qbzzt/20220815-erc20-safety-rails`. +4. Öffnen Sie **contracts > erc20-safety-rails.sol**. + +## Einen ERC-20-Vertrag erstellen {#creating-an-erc-20-contract} + +Bevor wir die Sicherheitsfunktionalität hinzufügen können, benötigen wir einen ERC-20-Vertrag. In diesem Artikel verwenden wir [den OpenZeppelin Contracts Wizard](https://docs.openzeppelin.com/contracts/5.x/wizard). Öffnen Sie es in einem anderen Browser und befolgen Sie diese Anweisungen: + +1. Wählen Sie **ERC20** aus. + +2. Geben Sie diese Einstellungen ein: + + | Parameter | Wert | + | ------------------ | ------------------------------- | + | Name | SafetyRailsToken | + | Symbol | SAFE | + | Premint | 1000 | + | Eigenschaften | Keine (None) | + | Zugriffskontrolle | Ownable | + | Aktualisierbarkeit | Keine (None) | + +3. Scrollen Sie nach oben und klicken Sie auf **In Remix öffnen** (für Remix) oder auf **Herunterladen**, um eine andere Umgebung zu verwenden. Ich gehe davon aus, dass Sie Remix verwenden. Wenn Sie etwas anderes verwenden, nehmen Sie einfach die entsprechenden Änderungen vor. + +4. Wir haben jetzt einen voll funktionsfähigen ERC-20-Vertrag. Sie können `.deps` > `npm` erweitern, um den importierten Code zu sehen. + +5. Kompilieren, deployen und spielen Sie mit dem Vertrag, um zu sehen, dass er als ERC-20-Vertrag funktioniert. Wenn Sie lernen müssen, wie man Remix benutzt, [verwenden Sie dieses Tutorial](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth). + +## Häufige Fehler {#common-mistakes} + +### Die Fehler {#the-mistakes} + +Benutzer senden manchmal Token an die falsche Adresse. Wir können zwar nicht ihre Gedanken lesen, um zu wissen, was sie vorhatten, aber es gibt zwei Fehlertypen, die häufig vorkommen und leicht zu erkennen sind: + +1. Senden der Token an die eigene Adresse des Vertrags. Zum Beispiel hat [Optimisms OP-Token](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c) in weniger als zwei Monaten [über 120.000](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000042) OP-Token angesammelt. Dies stellt ein beträchtliches Vermögen dar, das die Leute vermutlich einfach verloren haben. + +2. Senden der Token an eine leere Adresse, die weder einem [externen Konto](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) noch einem [Smart Contract](/developers/docs/smart-contracts) entspricht. Obwohl ich keine Statistiken darüber habe, wie oft dies vorkommt, [hätte ein Vorfall 20.000.000 Token kosten können](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595). + +### Verhindern von Überweisungen {#preventing-transfers} + +Der OpenZeppelin ERC-20-Vertrag enthält [einen Hook, `_beforeTokenTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368), der aufgerufen wird, bevor ein Token übertragen wird. Standardmäßig tut dieser Hook nichts, aber wir können unsere eigene Funktionalität daran hängen, wie z. B. Prüfungen, die zurücksetzen, wenn es ein Problem gibt. + +Um den Hook zu verwenden, fügen Sie diese Funktion nach dem Konstruktor hinzu: + +```solidity + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal virtual + override(ERC20) + { + super._beforeTokenTransfer(from, to, amount); + } +``` + +Einige Teile dieser Funktion sind vielleicht neu für Sie, wenn Sie mit Solidity nicht sehr vertraut sind: + +```solidity + internal virtual +``` + +Das Schlüsselwort `virtual` bedeutet, dass, so wie wir die Funktionalität von `ERC20` geerbt und diese Funktion überschrieben haben, andere Verträge von uns erben und diese Funktion überschreiben können. + +```solidity + override(ERC20) +``` + +Wir müssen explizit angeben, dass wir die ERC20-Token-Definition von `_beforeTokenTransfer` [überschreiben](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding). Im Allgemeinen sind explizite Definitionen aus Sicherheitssicht viel besser als implizite – man kann nicht vergessen, dass man etwas getan hat, wenn es direkt vor einem liegt. Das ist auch der Grund, warum wir angeben müssen, welche `_beforeTokenTransfer`-Funktion der Superklasse wir überschreiben. + +```solidity + super._beforeTokenTransfer(from, to, amount); +``` + +Diese Zeile ruft die `_beforeTokenTransfer`-Funktion des Vertrags oder der Verträge auf, von denen wir geerbt haben und die sie besitzen. In diesem Fall ist das nur `ERC20`, `Ownable` hat diesen Hook nicht. Auch wenn `ERC20._beforeTokenTransfer` derzeit nichts tut, rufen wir es auf, für den Fall, dass in Zukunft Funktionalität hinzugefügt wird (und wir uns dann entscheiden, den Vertrag neu zu deployen, da sich Verträge nach dem Deployment nicht ändern). + +### Kodierung der Anforderungen {#coding-the-requirements} + +Wir wollen der Funktion diese Anforderungen hinzufügen: + +- Die `to`-Adresse darf nicht `address(this)` sein, die Adresse des ERC-20-Vertrags selbst. +- Die `to`-Adresse darf nicht leer sein, sie muss entweder: + - Ein externes Konto (EOA). Wir können nicht direkt prüfen, ob eine Adresse ein EOA ist, aber wir können den ETH-Saldo einer Adresse prüfen. EOAs haben fast immer einen Saldo, auch wenn sie nicht mehr verwendet werden – es ist schwierig, sie bis auf den letzten Wei zu leeren. + - Ein Smart Contract. Zu testen, ob eine Adresse ein Smart Contract ist, ist etwas schwieriger. Es gibt einen Opcode, der die externe Codelänge prüft, namens [`EXTCODESIZE`](https://www.evm.codes/#3b), aber er ist in Solidity nicht direkt verfügbar. Wir müssen dafür [Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html) verwenden, was EVM-Assembly ist. Es gibt andere Werte, die wir von Solidity verwenden könnten ([`
.code` und `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)), aber sie kosten mehr. + +Gehen wir den neuen Code Zeile für Zeile durch: + +```solidity + require(to != address(this), "Token können nicht an die Vertragsadresse gesendet werden"); +``` + +Dies ist die erste Anforderung: Prüfen, dass `to` und `this(address)` nicht dasselbe sind. + +```solidity + bool isToContract; + assembly { + isToContract := gt(extcodesize(to), 0) + } +``` + +So prüfen wir, ob eine Adresse ein Vertrag ist. Wir können die Ausgabe nicht direkt von Yul empfangen, also definieren wir stattdessen eine Variable, die das Ergebnis enthält (in diesem Fall `isToContract`). Yul funktioniert so, dass jeder Opcode als eine Funktion betrachtet wird. Zuerst rufen wir also [`EXTCODESIZE`](https://www.evm.codes/#3b) auf, um die Vertragsgröße zu erhalten, und verwenden dann [`GT`](https://www.evm.codes/#11), um zu prüfen, ob sie nicht Null ist (wir haben es mit vorzeichenlosen Ganzzahlen zu tun, also kann sie natürlich nicht negativ sein). Wir schreiben dann das Ergebnis in `isToContract`. + +```solidity + require(to.balance != 0 || isToContract, "Token können nicht an eine leere Adresse gesendet werden"); +``` + +Und schließlich haben wir die eigentliche Prüfung auf leere Adressen. + +## Administrativer Zugriff {#admin-access} + +Manchmal ist es nützlich, einen Administrator zu haben, der Fehler rückgängig machen kann. Um das Missbrauchspotenzial zu verringern, kann dieser Administrator ein [Multisig](https://blog.logrocket.com/security-choices-multi-signature-wallets/) sein, sodass mehrere Personen einer Aktion zustimmen müssen. In diesem Artikel werden wir zwei administrative Funktionen haben: + +1. Einfrieren und Freigeben von Konten. Dies kann nützlich sein, zum Beispiel, wenn ein Konto kompromittiert sein könnte. +2. Asset-Bereinigung. + + Manchmal senden Betrüger betrügerische Token an den Vertrag des echten Tokens, um an Legitimität zu gewinnen. Zum Beispiel, [sehen Sie hier](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe?tab=holders). Der legitime ERC-20-Vertrag ist [0x4200....0042](https://optimism.blockscout.com/token/0x4200000000000000000000000000000000000042). Der Betrug, der vorgibt, er zu sein, ist [0x234....bbe](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe). + + Es ist auch möglich, dass Leute versehentlich legitime ERC-20-Token an unseren Vertrag senden, was ein weiterer Grund ist, eine Möglichkeit zu haben, sie herauszuholen. + +OpenZeppelin bietet zwei Mechanismen, um administrativen Zugriff zu ermöglichen: + +- [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable)-Verträge haben einen einzigen Besitzer. Funktionen, die den `onlyOwner`-[Modifikator](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) haben, können nur von diesem Besitzer aufgerufen werden. Besitzer können das Eigentum an jemand anderen übertragen oder vollständig darauf verzichten. Die Rechte aller anderen Konten sind typischerweise identisch. +- [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control)-Verträge haben eine [rollenbasierte Zugriffskontrolle (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control). + +Der Einfachheit halber verwenden wir in diesem Artikel `Ownable`. + +### Einfrieren und Auftauen von Verträgen {#freezing-and-thawing-contracts} + +Das Einfrieren und Auftauen von Verträgen erfordert mehrere Änderungen: + +- Ein [Mapping](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) von Adressen zu [Booleans](https://en.wikipedia.org/wiki/Boolean_data_type), um zu verfolgen, welche Adressen eingefroren sind. Alle Werte sind anfangs Null, was bei booleschen Werten als falsch interpretiert wird. Das ist, was wir wollen, denn standardmäßig sind Konten nicht eingefroren. + + ```solidity + mapping(address => bool) public frozenAccounts; + ``` + +- [Ereignisse](https://www.tutorialspoint.com/solidity/solidity_events.htm), um alle Interessierten zu informieren, wenn ein Konto eingefroren oder aufgetaut wird. Technisch gesehen sind Ereignisse für diese Aktionen nicht erforderlich, aber es hilft Offchain-Code, auf diese Ereignisse zu lauschen und zu wissen, was passiert. Es gilt als guter Stil, wenn ein Smart Contract sie ausgibt, wenn etwas passiert, das für jemand anderen relevant sein könnte. + + Die Ereignisse sind indiziert, so dass es möglich sein wird, nach allen Zeitpunkten zu suchen, an denen ein Konto eingefroren oder aufgetaut wurde. + + ```solidity + // Wenn Konten eingefroren oder aufgetaut werden + event AccountFrozen(address indexed _addr); + event AccountThawed(address indexed _addr); + ``` + +- Funktionen zum Einfrieren und Auftauen von Konten. Diese beiden Funktionen sind fast identisch, also werden wir nur die Einfrierfunktion durchgehen. + + ```solidity + function freezeAccount(address addr) + public + onlyOwner + ``` + + Funktionen, die als [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm) markiert sind, können von anderen Smart Contracts oder direkt durch eine Transaktion aufgerufen werden. + + ```solidity + { + require(!frozenAccounts[addr], "Konto bereits eingefroren"); + frozenAccounts[addr] = true; + emit AccountFrozen(addr); + } // freezeAccount + ``` + + Wenn das Konto bereits eingefroren ist, wird die Transaktion zurückgesetzt (revert). Andernfalls wird es eingefroren und ein Ereignis per `emit` ausgegeben. + +- Ändern Sie `_beforeTokenTransfer`, um zu verhindern, dass Geld von einem eingefrorenen Konto bewegt wird. Beachten Sie, dass Geld weiterhin auf das eingefrorene Konto überwiesen werden kann. + + ```solidity + require(!frozenAccounts[from], "Das Konto ist eingefroren"); + ``` + +### Asset-Bereinigung {#asset-cleanup} + +Um ERC-20-Token freizugeben, die von diesem Vertrag gehalten werden, müssen wir eine Funktion auf dem Token-Vertrag aufrufen, zu dem sie gehören, entweder [`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer) oder [`approve`](https://eips.ethereum.org/EIPS/eip-20#approve). Es hat keinen Sinn, in diesem Fall Gas für Freigaben (allowances) zu verschwenden, wir können genauso gut direkt übertragen. + +```solidity + function cleanupERC20( + address erc20, + address dest + ) + public + onlyOwner + { + IERC20 token = IERC20(erc20); +``` + +Dies ist die Syntax, um ein Objekt für einen Vertrag zu erstellen, wenn wir die Adresse erhalten. Wir können dies tun, weil wir die Definition für ERC20-Token als Teil des Quellcodes haben (siehe Zeile 4), und diese Datei enthält [die Definition für IERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol), die Schnittstelle für einen OpenZeppelin ERC-20-Vertrag. + +```solidity + uint balance = token.balanceOf(address(this)); + token.transfer(dest, balance); + } +``` + +Dies ist eine Bereinigungsfunktion, also wollen wir vermutlich keine Token übrig lassen. Anstatt den Saldo manuell vom Benutzer abzurufen, können wir den Prozess auch automatisieren. + +## Fazit {#conclusion} + +Dies ist keine perfekte Lösung – es gibt keine perfekte Lösung für das Problem \"Benutzer hat einen Fehler gemacht\". Die Verwendung dieser Art von Überprüfungen kann jedoch zumindest einige Fehler verhindern. Die Möglichkeit, Konten einzufrieren, ist zwar gefährlich, kann aber genutzt werden, um den Schaden bestimmter Hacks zu begrenzen, indem dem Hacker die gestohlenen Gelder verweigert werden. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/ethereum-for-web2-auth/index.md b/public/content/translations/de/developers/tutorials/ethereum-for-web2-auth/index.md new file mode 100644 index 00000000000..6d4b98cb917 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/ethereum-for-web2-auth/index.md @@ -0,0 +1,886 @@ +--- +title: "Verwendung von Ethereum für die Web2-Authentifizierung" +description: "Nach der Lektüre dieses Tutorials kann ein Entwickler die Ethereum-Anmeldung (Web3) in die SAML-Anmeldung integrieren, einen in Web2 verwendeten Standard zur Bereitstellung von Single Sign-On und anderen damit verbundenen Diensten. Dies ermöglicht die Authentifizierung des Zugriffs auf Web2-Ressourcen durch Ethereum-Signaturen, wobei die Benutzerattribute aus Attestierungen stammen." +author: Ori Pomerantz +tags: [ "web2", "authentifizierung", "eas" ] +skill: beginner +lang: de +published: 2025-04-30 +--- + +## Einführung + +[SAML](https://www.onelogin.com/learn/saml) ist ein in Web2 verwendeter Standard, der es einem [Identitätsanbieter (IdP)](https://en.wikipedia.org/wiki/Identity_provider#SAML_identity_provider) ermöglicht, Benutzerinformationen für [Dienstanbieter (SP)](https://en.wikipedia.org/wiki/Service_provider_\(SAML\)) bereitzustellen. + +In diesem Tutorial lernen Sie, wie Sie Ethereum-Signaturen in SAML integrieren, damit Benutzer ihre Ethereum-Wallets zur Authentifizierung bei Web2-Diensten verwenden können, die Ethereum noch nicht nativ unterstützen. + +Beachten Sie, dass dieses Tutorial für zwei verschiedene Zielgruppen geschrieben wurde: + +- Ethereum-Leute, die Ethereum verstehen und SAML lernen müssen +- Web2-Leute, die SAML und die Web2-Authentifizierung verstehen und Ethereum lernen müssen + +Daher wird es eine Menge Einführungsmaterial enthalten, das Sie bereits kennen. Sie können es gerne überspringen. + +### SAML für Ethereum-Leute + +SAML ist ein zentralisiertes Protokoll. Ein Dienstanbieter (SP) akzeptiert nur dann Zusicherungen (z. B. „Dies ist mein Benutzer John, er sollte die Berechtigungen haben, A, B und C zu tun“) von einem Identitätsanbieter (IdP), wenn er eine bereits bestehende Vertrauensbeziehung entweder zu diesem oder zu der [Zertifizierungsstelle](https://www.ssl.com/article/what-is-a-certificate-authority-ca/) hat, die das Zertifikat dieses IdP unterzeichnet hat. + +Der SP kann beispielsweise ein Reisebüro sein, das Reisedienstleistungen für Unternehmen anbietet, und der IdP kann die interne Website eines Unternehmens sein. Wenn Mitarbeiter eine Geschäftsreise buchen müssen, schickt das Reisebüro sie zur Authentifizierung an das Unternehmen, bevor es sie tatsächlich die Reise buchen lässt. + +![Schritt-für-Schritt-SAML-Prozess](./fig-01-saml.png) + +Auf diese Weise verhandeln die drei Entitäten, der Browser, der SP und der IdP, über den Zugriff. Der SP muss im Voraus nichts über den Benutzer wissen, der den Browser verwendet, sondern nur dem IdP vertrauen. + +### Ethereum für SAML-Leute + +Ethereum ist ein dezentralisiertes System. + +![Ethereum-Anmeldung](./fig-02-eth-logon.png) + +Benutzer haben einen privaten Schlüssel (in der Regel in einer Browser-Erweiterung). Aus dem privaten Schlüssel können Sie einen öffentlichen Schlüssel und daraus eine 20-Byte-Adresse ableiten. Wenn sich Benutzer in einem System anmelden müssen, werden sie aufgefordert, eine Nachricht mit einer Nonce (einem einmalig verwendbaren Wert) zu signieren. Der Server kann überprüfen, ob die Signatur von dieser Adresse erstellt wurde. + +![Zusätzliche Daten aus Attestierungen erhalten](./fig-03-eas-data.png) + +Die Signatur verifiziert nur die Ethereum-Adresse. Um andere Benutzerattribute zu erhalten, verwenden Sie normalerweise [Attestierungen](https://attest.org/). Eine Attestierung hat normalerweise diese Felder: + +- **Attestierer**, die Adresse, die die Attestierung vorgenommen hat +- **Empfänger**, die Adresse, für die die Attestierung gilt +- **Daten**, die zu bescheinigenden Daten, wie Name, Berechtigungen usw. +- **Schema**, die ID des Schemas, das zur Interpretation der Daten verwendet wird. + +Aufgrund der dezentralen Natur von Ethereum kann jeder Benutzer Attestierungen erstellen. Die Identität des Attestierers ist wichtig, um zu bestimmen, welche Attestierungen wir als zuverlässig betrachten. + +## Einrichtung + +Der erste Schritt besteht darin, einen SAML-SP und einen SAML-IdP zu haben, die miteinander kommunizieren. + +1. Laden Sie die Software herunter. Die Beispielsoftware für diesen Artikel ist [auf GitHub](https://github.com/qbzzt/250420-saml-ethereum) verfügbar. Verschiedene Phasen werden in verschiedenen Branches gespeichert, für diese Phase benötigen Sie `saml-only` + + ```sh + git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only + cd 250420-saml-ethereum + pnpm install + ``` + +2. Erstellen Sie Schlüssel mit selbstsignierten Zertifikaten. Das bedeutet, dass der Schlüssel seine eigene Zertifizierungsstelle ist und manuell beim Dienstanbieter importiert werden muss. Weitere Informationen finden Sie in den [OpenSSL-Dokumenten](https://docs.openssl.org/master/man1/openssl-req/). + + ```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. Starten Sie die Server (sowohl SP als auch IdP) + + ```sh + pnpm start + ``` + +4. Navigieren Sie zum SP unter der URL [http://localhost:3000/](http://localhost:3000/) und klicken Sie auf die Schaltfläche, um zum IdP (Port 3001) weitergeleitet zu werden. + +5. Geben Sie dem IdP Ihre E-Mail-Adresse und klicken Sie auf **Beim Dienstanbieter anmelden**. Sehen Sie, dass Sie zum Dienstanbieter (Port 3000) zurückgeleitet werden und dass dieser Sie anhand Ihrer E-Mail-Adresse erkennt. + +### Detaillierte Erklärung + +Folgendes geschieht Schritt für Schritt: + +![Normale SAML-Anmeldung ohne Ethereum](./fig-04-saml-no-eth.png) + +#### src/config.mts + +Diese Datei enthält die Konfiguration sowohl für den Identitätsanbieter als auch für den Dienstanbieter. Normalerweise wären dies zwei verschiedene Entitäten, aber hier können wir der Einfachheit halber Code gemeinsam nutzen. + +```typescript +const fs = await import("fs") + +const protocol="http" +``` + +Im Moment testen wir nur, daher ist die Verwendung von HTTP in Ordnung. + +```typescript +export const spCert = fs.readFileSync("keys/saml-sp.crt").toString() +export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString() +``` + +Lesen Sie die öffentlichen Schlüssel, die normalerweise beiden Komponenten zur Verfügung stehen (und denen entweder direkt vertraut wird oder die von einer vertrauenswürdigen Zertifizierungsstelle signiert sind). + +```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}` +``` + +Die URLs für beide Komponenten. + +```typescript +export const spPublicData = { +``` + +Die öffentlichen Daten für den Dienstanbieter. + +```typescript + entityID: `${spUrl}/metadata`, +``` + +Konventionell ist in SAML die `entityID` die URL, unter der die Metadaten der Entität verfügbar sind. Diese Metadaten entsprechen den hier vorliegenden öffentlichen Daten, außer dass sie in XML-Form vorliegen. + +```typescript + wantAssertionsSigned: true, + authnRequestsSigned: false, + signingCert: spCert, + allowCreate: true, + assertionConsumerService: [{ + Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + Location: `${spUrl}/assertion`, + }] + } +``` + +Die wichtigste Definition für unsere Zwecke ist der `assertionConsumerServer`. Das bedeutet, dass wir, um etwas gegenüber dem Dienstanbieter zu behaupten (z. B. „Der Benutzer, der Ihnen diese Informationen sendet, ist jemand@example.com“), einen [HTTP-POST](https://www.w3schools.com/tags/ref_httpmethods.asp) an die URL `http://localhost:3000/sp/assertion` verwenden müssen. + +```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` + }], + } +``` + +Die öffentlichen Daten für den Identitätsanbieter sind ähnlich. Es gibt an, dass Sie zum Anmelden eines Benutzers einen POST an `http://localhost:3001/idp/login` und zum Abmelden eines Benutzers einen POST an `http://localhost:3001/idp/logout` senden. + +#### src/sp.mts + +Dies ist der Code, der einen Dienstanbieter implementiert. + +```typescript +import * as config from "./config.mts" +const fs = await import("fs") +const saml = await import("samlify") +``` + +Wir verwenden die Bibliothek [`samlify`](https://www.npmjs.com/package/samlify), um SAML zu implementieren. + +```typescript +import * as validator from "@authenio/samlify-node-xmllint" +saml.setSchemaValidator(validator) +``` + +Die `samlify`-Bibliothek erwartet ein Paket, das validiert, dass das XML korrekt ist, mit dem erwarteten öffentlichen Schlüssel signiert ist usw. Wir verwenden [`@authenio/samlify-node-xmllint`](https://www.npmjs.com/package/@authenio/samlify-node-xmllint) für diesen Zweck. + +```typescript +const express = (await import("express")).default +const spRouter = express.Router() +const app = express() +``` + +Ein [`express`](https://expressjs.com/)-[`Router`](https://expressjs.com/en/5x/api.html#router) ist eine „Mini-Website“, die innerhalb einer Website eingebunden werden kann. In diesem Fall verwenden wir ihn, um alle Definitionen des Dienstanbieters zusammenzufassen. + +```typescript +const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString() + +const sp = saml.ServiceProvider({ + privateKey: spPrivateKey, + ...config.spPublicData +}) +``` + +Die Eigendarstellung des Dienstanbieters besteht aus allen öffentlichen Daten und dem privaten Schlüssel, den er zum Signieren von Informationen verwendet. + +```typescript +const idp = saml.IdentityProvider(config.idpPublicData); +``` + +Die öffentlichen Daten enthalten alles, was der Dienstanbieter über den Identitätsanbieter wissen muss. + +```typescript +spRouter.get(`/metadata`, + (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata()) +) +``` + +Um die Interoperabilität mit anderen SAML-Komponenten zu ermöglichen, sollten Dienst- und Identitätsanbieter ihre öffentlichen Daten (die Metadaten) im XML-Format unter `/metadata` zur Verfügung stellen. + +```typescript +spRouter.post(`/assertion`, +``` + +Dies ist die Seite, auf die der Browser zugreift, um sich zu identifizieren. Die Zusicherung enthält die Benutzerkennung (hier verwenden wir die E-Mail-Adresse) und kann zusätzliche Attribute enthalten. Dies ist der Handler für Schritt 7 im obigen Sequenzdiagramm. + +```typescript + async (req, res) => { + // console.log(`SAML response:\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}) +``` + +Sie können den auskommentierten Befehl verwenden, um die in der Zusicherung bereitgestellten XML-Daten zu sehen. Sie ist [Base64-kodiert](https://en.wikipedia.org/wiki/Base64). + +```typescript + try { + const loginResponse = await sp.parseLoginResponse(idp, 'post', req); +``` + +Analysieren Sie die Anmeldeanforderung vom Identitätsserver. + +```typescript + res.send(` + + +

Hallo ${loginResponse.extract.nameID}

+ + + `) + res.send(); +``` + +Senden Sie eine HTML-Antwort, nur um dem Benutzer zu zeigen, dass wir die Anmeldung erhalten haben. + +```typescript + } catch (err) { + console.error('Fehler bei der Verarbeitung der SAML-Antwort:', err); + res.status(400).send('SAML-Authentifizierung fehlgeschlagen'); + } + } +) +``` + +Informieren Sie den Benutzer im Falle eines Fehlers. + +```typescript +spRouter.get('/login', +``` + +Erstellen Sie eine Anmeldeanforderung, wenn der Browser versucht, diese Seite aufzurufen. Dies ist der Handler für Schritt 1 im obigen Sequenzdiagramm. + +```typescript + async (req, res) => { + const loginRequest = await sp.createLoginRequest(idp, "post") +``` + +Holen Sie sich die Informationen, um eine Anmeldeanforderung zu posten. + +```typescript + res.send(` + + + +``` + +Diese Seite sendet das Formular (siehe unten) automatisch ab. Auf diese Weise muss der Benutzer nichts tun, um weitergeleitet zu werden. Dies ist Schritt 2 im obigen Sequenzdiagramm. + +```typescript +
+``` + +Posten Sie an `loginRequest.entityEndpoint` (die URL des Endpunkts des Identitätsanbieters). + +```typescript + +``` + +Der Eingabename ist `loginRequest.type` (`SAMLRequest`). Der Inhalt für dieses Feld ist `loginRequest.context`, was wiederum XML ist, das Base64-kodiert ist. + +```typescript +
+ + + `) + } +) + +app.use(express.urlencoded({extended: true})) +``` + +[Diese Middleware](https://expressjs.com/en/5x/api.html#express.urlencoded) liest den Body der [HTTP-Anfrage](https://www.tutorialspoint.com/http/http_requests.htm). Standardmäßig ignoriert Express ihn, da die meisten Anfragen ihn nicht benötigen. Wir brauchen ihn, weil POST den Body verwendet. + +```typescript +app.use(`/${config.spDir}`, spRouter) +``` + +Binden Sie den Router im Verzeichnis des Dienstanbieters (`/sp`) ein. + +```typescript +app.get("/", (req, res) => { + res.send(` + + + + + + `) +}) +``` + +Wenn ein Browser versucht, das Stammverzeichnis aufzurufen, stellen Sie ihm einen Link zur Anmeldeseite zur Verfügung. + +```typescript +app.listen(config.spPort, () => { + console.log(`Dienstanbieter läuft auf http://${config.spHostname}:${config.spPort}`) +}) +``` + +Hören Sie mit dieser Express-Anwendung auf den `spPort`. + +#### src/idp.mts + +Dies ist der Identitätsanbieter. Er ist dem Dienstanbieter sehr ähnlich, die folgenden Erklärungen beziehen sich auf die Teile, die sich unterscheiden. + +```typescript +const xmlParser = new (await import("fast-xml-parser")).XMLParser( + { + ignoreAttributes: false, // Attribute beibehalten + attributeNamePrefix: "@_", // Präfix für Attribute + } +) +``` + +Wir müssen die XML-Anforderung, die wir vom Dienstanbieter erhalten, lesen und verstehen. + +```typescript +const getLoginPage = requestId => ` +``` + +Diese Funktion erstellt die Seite mit dem automatisch übermittelten Formular, das in Schritt 4 des obigen Sequenzdiagramms zurückgegeben wird. + +```typescript + + + Anmeldeseite + + +

Anmeldeseite

+
+ + E-Mail-Adresse: +
+ +``` + +Es gibt zwei Felder, die wir an den Dienstanbieter senden: + +1. Die `requestId`, auf die wir antworten. +2. Die Benutzerkennung (wir verwenden vorerst die E-Mail-Adresse, die der Benutzer angibt). + +```typescript +
+ + + +const idpRouter = express.Router() + +idpRouter.post("/loginSubmitted", async (req, res) => { + const loginResponse = await idp.createLoginResponse( +``` + +Dies ist der Handler für Schritt 5 des obigen Sequenzdiagramms. [`idp.createLoginResponse`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L73-L125) erstellt die Anmeldeantwort. + +```typescript + sp, + { + authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + audience: sp.entityID, +``` + +Die Zielgruppe ist der Dienstanbieter. + +```typescript + extract: { + request: { + id: req.body.requestId + } + }, +``` + +Aus der Anfrage extrahierte Informationen. Der einzige Parameter, der uns in der Anfrage interessiert, ist die requestId, die es dem Dienstanbieter ermöglicht, Anfragen und deren Antworten zuzuordnen. + +```typescript + signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Signierung sicherstellen +``` + +Wir benötigen `signingKey`, um die Daten zum Signieren der Antwort zu haben. Der Dienstanbieter vertraut keinen unsignierten Anfragen. + +```typescript + }, + "post", + { + email: req.body.email +``` + +Dies ist das Feld mit den Benutzerinformationen, die wir an den Dienstanbieter zurücksenden. + +```typescript + } + ); + + res.send(` + + + + +
+ +
+ + + `) +}) +``` + +Verwenden Sie erneut ein automatisch übermitteltes Formular. Dies ist Schritt 6 im obigen Sequenzdiagramm. + +```typescript + +// IdP-Endpunkt für Anmeldeanforderungen +idpRouter.post(`/login`, +``` + +Dies ist der Endpunkt, der eine Anmeldeanforderung vom Dienstanbieter empfängt. Dies ist der Handler für Schritt 3 des obigen Sequenzdiagramms. + +```typescript + async (req, res) => { + try { + // Workaround, weil ich parseLoginRequest nicht zum Laufen bringen konnte. + // 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"])) +``` + +Wir sollten [`idp.parseLoginRequest`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L127-L144) verwenden können, um die ID der Authentifizierungsanforderung zu lesen. Ich konnte es jedoch nicht zum Laufen bringen und es war nicht wert, viel Zeit damit zu verbringen, also verwende ich einfach einen [allgemeinen XML-Parser](https://www.npmjs.com/package/fast-xml-parser). Die Information, die wir benötigen, ist das `ID`-Attribut innerhalb des ``-Tags, das sich auf der obersten Ebene des XML befindet. + +## Verwendung von Ethereum-Signaturen + +Nachdem wir nun eine Benutzeridentität an den Dienstanbieter senden können, besteht der nächste Schritt darin, die Benutzeridentität auf vertrauenswürdige Weise zu erhalten. Viem ermöglicht es uns, die Wallet einfach nach der Benutzeradresse zu fragen, aber das bedeutet, den Browser nach den Informationen zu fragen. Wir kontrollieren den Browser nicht, daher können wir der Antwort, die wir von ihm erhalten, nicht automatisch vertrauen. + +Stattdessen wird der IdP dem Browser eine zu signierende Zeichenfolge senden. Wenn die Wallet im Browser diese Zeichenfolge signiert, bedeutet dies, dass es sich wirklich um diese Adresse handelt (d. h. sie kennt den privaten Schlüssel, der der Adresse entspricht). + +Um dies in Aktion zu sehen, stoppen Sie den vorhandenen IdP und SP und führen Sie diese Befehle aus: + +```sh +git checkout eth-signatures +pnpm install +pnpm start +``` + +Navigieren Sie dann [zum SP](http://localhost:3000) und folgen Sie den Anweisungen. + +Beachten Sie, dass wir an dieser Stelle nicht wissen, wie wir die E-Mail-Adresse aus der Ethereum-Adresse erhalten, also melden wir stattdessen `@bad.email.address` an den SP. + +### Detaillierte Erklärung + +Die Änderungen befinden sich in den Schritten 4-5 des vorherigen Diagramms. + +![SAML mit einer Ethereum-Signatur](./fig-05-saml-w-signature.png) + +Die einzige Datei, die wir geändert haben, ist `idp.mts`. Hier sind die geänderten Teile. + +```typescript +import { v4 as uuidv4 } from 'uuid' +import { verifyMessage } from 'viem' +``` + +Wir benötigen diese beiden zusätzlichen Bibliotheken. Wir verwenden [`uuid`](https://www.npmjs.com/package/uuid), um den [Nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce)-Wert zu erstellen. Der Wert selbst spielt keine Rolle, nur die Tatsache, dass er nur einmal verwendet wird. + +Die Bibliothek [`viem`](https://viem.sh/) ermöglicht es uns, Ethereum-Definitionen zu verwenden. Hier benötigen wir sie, um zu überprüfen, ob die Signatur tatsächlich gültig ist. + +```typescript +const loginPrompt = "Um auf den Dienstanbieter zuzugreifen, signieren Sie diese Nonce: " +``` + +Die Wallet bittet den Benutzer um die Erlaubnis, die Nachricht zu signieren. Eine Nachricht, die nur eine Nonce ist, könnte Benutzer verwirren, daher fügen wir diese Aufforderung hinzu. + +```typescript +// requestIDs hier aufbewahren +let nonces = {} +``` + +Wir benötigen die Anfrageinformationen, um darauf antworten zu können. Wir könnten sie mit der Anfrage (Schritt 4) senden und sie zurückerhalten (Schritt 5). Wir können jedoch den Informationen, die wir vom Browser erhalten, nicht vertrauen, da dieser unter der Kontrolle eines potenziell feindseligen Benutzers steht. Daher ist es besser, sie hier mit der Nonce als Schlüssel zu speichern. + +Beachten Sie, dass wir dies der Einfachheit halber hier als Variable tun. Dies hat jedoch mehrere Nachteile: + +- Wir sind anfällig für einen Denial-of-Service-Angriff. Ein böswilliger Benutzer könnte versuchen, sich mehrmals anzumelden und so unseren Speicher zu füllen. +- Wenn der IdP-Prozess neu gestartet werden muss, verlieren wir die vorhandenen Werte. +- Wir können keinen Lastenausgleich über mehrere Prozesse durchführen, da jeder seine eigene Variable hätte. + +Auf einem Produktionssystem würden wir eine Datenbank verwenden und eine Art Ablaufmechanismus implementieren. + +```typescript +const getSignaturePage = requestId => { + const nonce = uuidv4() + nonces[nonce] = requestId +``` + +Erstellen Sie eine Nonce und speichern Sie die `requestId` für die zukünftige Verwendung. + +```typescript + return ` + + + + + +

Bitte signieren

+ +
+ + + +` +} +``` + +Der Rest ist nur Standard-HTML. + +```typescript +idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => { +``` + +Dies ist der Handler für Schritt 5 im Sequenzdiagramm. + +```typescript + const requestId = nonces[req.params.nonce] + if (requestId === undefined) { + res.send("Schlechte Nonce") + return ; + } + + nonces[req.params.nonce] = undefined +``` + +Holen Sie sich die Anfrage-ID und löschen Sie die Nonce aus den `nonces`, um sicherzustellen, dass sie nicht wiederverwendet werden kann. + +```typescript + try { +``` + +Da es so viele Möglichkeiten gibt, wie die Signatur ungültig sein kann, umschließen wir dies in einem `try ...` `catch`-Block, um alle geworfenen Fehler abzufangen. + +```typescript + const validSignature = await verifyMessage({ + address: req.params.account, + message: `${loginPrompt}${req.params.nonce}`, + signature: req.params.signature + }) +``` + +Verwenden Sie [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage), um Schritt 5.5 im Sequenzdiagramm zu implementieren. + +```typescript + if (!validSignature) + throw("Schlechte Signatur") + } catch (err) { + res.send("Fehler:" + err) + return ; + } +``` + +Der Rest des Handlers ist äquivalent zu dem, was wir zuvor im `/loginSubmitted`-Handler getan haben, mit Ausnahme einer kleinen Änderung. + +```typescript + const loginResponse = await idp.createLoginResponse( + . + . + . + { + email: req.params.account + "@bad.email.address" + } + ); +``` + +Wir haben nicht die tatsächliche E-Mail-Adresse (wir werden sie im nächsten Abschnitt erhalten), also geben wir vorerst die Ethereum-Adresse zurück und kennzeichnen sie deutlich als keine E-Mail-Adresse. + +```typescript +// IdP-Endpunkt für Anmeldeanforderungen +idpRouter.post(`/login`, + async (req, res) => { + try { + // Workaround, weil ich parseLoginRequest nicht zum Laufen bringen konnte. + // 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('Fehler bei der Verarbeitung der SAML-Antwort:', err); + res.status(400).send('SAML-Authentifizierung fehlgeschlagen'); + } + } +) +``` + +Verwenden Sie jetzt anstelle von `getLoginPage` `getSignaturePage` im Handler für Schritt 3. + +## Abrufen der E-Mail-Adresse + +Der nächste Schritt ist das Abrufen der E-Mail-Adresse, der vom Dienstanbieter angeforderten Kennung. Dazu verwenden wir den [Ethereum Attestation Service (EAS)](https://attest.org/). + +Der einfachste Weg, Attestierungen zu erhalten, ist die Verwendung der [GraphQL-API](https://docs.attest.org/docs/developer-tools/api). Wir verwenden diese Abfrage: + +``` +query GetAttestationsByRecipient { + attestations( + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } + take: 1 + ) { + data + id + attester + } +} +``` + +Diese [`schemaId`](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977) enthält nur eine E-Mail-Adresse. Diese Abfrage fragt nach Attestierungen dieses Schemas. Das Subjekt der Attestierung wird als `Empfänger` bezeichnet. Es ist immer eine Ethereum-Adresse. + +Warnung: Die Art und Weise, wie wir hier Attestierungen erhalten, hat zwei Sicherheitsprobleme. + +- Wir greifen auf den API-Endpunkt `https://optimism.easscan.org/graphql` zu, der eine zentralisierte Komponente ist. Wir können das `id`-Attribut erhalten und dann eine On-Chain-Abfrage durchführen, um zu überprüfen, ob eine Attestierung echt ist, aber der API-Endpunkt kann immer noch Attestierungen zensieren, indem er uns nicht darüber informiert. + + Dieses Problem ist nicht unlösbar, wir könnten unseren eigenen GraphQL-Endpunkt betreiben und die Attestierungen aus den Chain-Logs abrufen, aber das ist für unsere Zwecke übertrieben. + +- Wir schauen nicht auf die Identität des Attestierers. Jeder kann uns falsche Informationen liefern. In einer realen Implementierung hätten wir eine Reihe vertrauenswürdiger Attestierer und würden nur deren Attestierungen betrachten. + +Um dies in Aktion zu sehen, stoppen Sie den vorhandenen IdP und SP und führen Sie diese Befehle aus: + +```sh +git checkout email-address +pnpm install +pnpm start +``` + +Geben Sie dann Ihre E-Mail-Adresse an. Sie haben zwei Möglichkeiten, dies zu tun: + +- Importieren Sie eine Wallet mit einem privaten Schlüssel und verwenden Sie den privaten Testschlüssel `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`. + +- Fügen Sie eine Attestierung für Ihre eigene E-Mail-Adresse hinzu: + + 1. Navigieren Sie zu [dem Schema im Attestierungs-Explorer](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977). + + 2. Klicken Sie auf **Mit Schema attestieren**. + + 3. Geben Sie Ihre Ethereum-Adresse als Empfänger und Ihre E-Mail-Adresse als E-Mail-Adresse ein und wählen Sie **On-Chain**. Klicken Sie dann auf **Attestierung erstellen**. + + 4. Genehmigen Sie die Transaktion in Ihrer Wallet. Sie benötigen etwas ETH auf der [Optimism Blockchain](https://app.optimism.io/bridge/deposit), um für Gas zu bezahlen. + +So oder so, navigieren Sie danach zu [http://localhost:3000](http://localhost:3000) und folgen Sie den Anweisungen. Wenn Sie den privaten Testschlüssel importiert haben, lautet die E-Mail, die Sie erhalten, `test_addr_0@example.com`. Wenn Sie Ihre eigene Adresse verwendet haben, sollte es das sein, was Sie attestiert haben. + +### Detaillierte Erklärung + +![Von der Ethereum-Adresse zur E-Mail gelangen](./fig-06-saml-sig-n-email.png) + +Die neuen Schritte sind die GraphQL-Kommunikation, Schritte 5.6 und 5.7. + +Auch hier sind die geänderten Teile von `idp.mts`. + +```typescript +import { GraphQLClient } from 'graphql-request' +import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk' +``` + +Importieren Sie die Bibliotheken, die wir benötigen. + +```typescript +const graphqlEndpointUrl = "https://optimism.easscan.org/graphql" +``` + +Es gibt [einen separaten Endpunkt für jede Blockchain](https://docs.attest.org/docs/developer-tools/api). + +```typescript +const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch }) +``` + +Erstellen Sie einen neuen `GraphQLClient`-Client, den wir zur Abfrage des Endpunkts verwenden können. + +```typescript +const graphqlSchema = 'string emailAddress' +const graphqlEncoder = new SchemaEncoder(graphqlSchema) +``` + +GraphQL gibt uns nur ein undurchsichtiges Datenobjekt mit Bytes. Um es zu verstehen, benötigen wir das Schema. + +```typescript +const ethereumAddressToEmail = async ethAddr => { +``` + +Eine Funktion, um von einer Ethereum-Adresse zu einer E-Mail-Adresse zu gelangen. + +```typescript + const query = ` + query GetAttestationsByRecipient { +``` + +Dies ist eine GraphQL-Abfrage. + +```typescript + Attestierungen( +``` + +Wir suchen nach Attestierungen. + +```typescript + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } +``` + +Die Attestierungen, die wir wollen, sind diejenigen in unserem Schema, bei denen der Empfänger `getAddress(ethAddr)` ist. Die Funktion [`getAddress`](https://viem.sh/docs/utilities/getAddress#getaddress) stellt sicher, dass unsere Adresse die korrekte [Prüfsumme](https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md) hat. Dies ist notwendig, da bei GraphQL die Groß- und Kleinschreibung beachtet wird. "0xBAD060A7", "0xBad060A7" und "0xbad060a7" sind verschiedene Werte. + +```typescript + take: 1 +``` + +Unabhängig davon, wie viele Attestierungen wir finden, wollen wir nur die erste. + +```typescript + ) { + data + id + attester + } + }` +``` + +Die Felder, die wir empfangen wollen. + +- `attester`: Die Adresse, die die Attestierung eingereicht hat. Normalerweise wird dies verwendet, um zu entscheiden, ob der Attestierung vertraut werden soll oder nicht. +- `id`: Die Attestierungs-ID. Sie können diesen Wert verwenden, um [die Attestierung On-Chain zu lesen](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000021?tab=read_proxy&source_address=0x4E0275Ea5a89e7a3c1B58411379D1a0eDdc5b088#0xa3112a64) und zu überprüfen, ob die Informationen aus der GraphQL-Abfrage korrekt sind. +- `data`: Die Schemadaten (in diesem Fall die E-Mail-Adresse). + +```typescript + const queryResult = await graphqlClient.request(query) + + if (queryResult.attestations.length == 0) + return "no_address@available.is" +``` + +Wenn keine Attestierung vorhanden ist, geben Sie einen Wert zurück, der offensichtlich falsch ist, aber für den Dienstanbieter gültig erscheinen würde. + +```typescript + const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data) + return attestationDataFields[0].value.value +} +``` + +Wenn ein Wert vorhanden ist, verwenden Sie `decodeData`, um die Daten zu dekodieren. Wir benötigen nicht die Metadaten, die es bereitstellt, sondern nur den Wert selbst. + +```typescript + const loginResponse = await idp.createLoginResponse( + sp, + { + . + . + . + }, + "post", + { + email: await ethereumAddressToEmail(req.params.account) + } + ); +``` + +Verwenden Sie die neue Funktion, um die E-Mail-Adresse abzurufen. + +## Was ist mit der Dezentralisierung? + +In dieser Konfiguration können sich Benutzer nicht als jemand ausgeben, der sie nicht sind, solange wir uns auf vertrauenswürdige Attestierer für die Zuordnung von Ethereum- zu E-Mail-Adressen verlassen. Unser Identitätsanbieter ist jedoch immer noch eine zentralisierte Komponente. Wer den privaten Schlüssel des Identitätsanbieters besitzt, kann falsche Informationen an den Dienstanbieter senden. + +Es könnte eine Lösung geben, die [Mehrparteienberechnung (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation) verwendet. Ich hoffe, in einem zukünftigen Tutorial darüber zu schreiben. + +## Zusammenfassung + +Die Einführung eines Anmeldestandards wie Ethereum-Signaturen steht vor einem Henne-Ei-Problem. Dienstanbieter wollen den größtmöglichen Markt ansprechen. Benutzer möchten auf Dienste zugreifen können, ohne sich Gedanken über die Unterstützung ihres Anmeldestandards machen zu müssen. +Die Erstellung von Adaptern, wie z. B. einem Ethereum-IdP, kann uns helfen, diese Hürde zu überwinden. + +[Hier finden Sie mehr von meiner Arbeit](https://cryptodocguy.pro/). diff --git a/public/content/translations/de/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md b/public/content/translations/de/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md new file mode 100644 index 00000000000..1f097052b0a --- /dev/null +++ b/public/content/translations/de/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md @@ -0,0 +1,156 @@ +--- +title: Erste Schritte in der Ethereum-Entwicklung +description: "Dies ist ein Leitfaden für Einsteiger in die Ethereum-Entwicklung. Wir führen dich vom Einrichten eines API-Endpunkts über eine Befehlszeilenanforderung bis hin zum Schreiben deines ersten Web3-Skripts! Es sind keine Vorkenntnisse in der Blockchain-Entwicklung erforderlich!" +author: "Elan Halpern" +tags: + [ + "javascript", + "ethers.js", + "Nodes", + "Abfragen", + "Alchemy" + ] +skill: beginner +lang: de +published: 2020-10-30 +source: Medium +sourceUrl: https://medium.com/alchemy-api/getting-started-with-ethereum-development-using-alchemy-c3d6a45c567f +--- + +![Logos von Ethereum und Alchemy](./ethereum-alchemy.png) + +Dies ist ein Anfängerleitfaden für den Einstieg in die Ethereum-Entwicklung. Für dieses Tutorial verwenden wir [Alchemy](https://alchemyapi.io/), die führende Blockchain-Entwicklerplattform, die Millionen von Nutzern von 70 % der Top-Blockchain-Apps wie Maker, 0x, MyEtherWallet, Dharma und Kyber antreibt. Alchemy gibt uns Zugriff auf einen API-Endpunkt auf der Ethereum-Chain, damit wir Transaktionen lesen und schreiben können. + +Wir zeigen dir alle Schritte von der Anmeldung bei Alchemy bis hin zum Schreiben deines ersten Web3-Skripts! Es sind keine Vorkenntnisse in der Blockchain-Entwicklung erforderlich! + +## 1. Registriere dich für ein kostenloses Alchemy-Konto {#sign-up-for-a-free-alchemy-account} + +Ein Konto bei Alchemy zu erstellen ist einfach, [registriere dich hier kostenlos](https://auth.alchemy.com/). + +## 2. Erstelle eine Alchemy-App {#create-an-alchemy-app} + +Um mit der Ethereum-Chain zu kommunizieren und die Produkte von Alchemy zu nutzen, benötigst du einen API-Schlüssel, um deine Anfragen zu authentifizieren. + +Du kannst [API-Schlüssel über das Dashboard erstellen](https://dashboard.alchemy.com/). Um einen neuen Schlüssel zu erstellen, navigiere zu „App erstellen“, wie unten gezeigt: + +Besonderer Dank an [_ShapeShift_](https://shapeshift.com/), _dass wir ihr Dashboard zeigen dürfen!_ + +![Alchemy-Dashboard](./alchemy-dashboard.png) + +Fülle die Details unter „App erstellen“ aus, um deinen neuen Schlüssel zu erhalten. Hier kannst du auch Apps sehen, die du zuvor erstellt hast, und solche, die von deinem Team erstellt wurden. Bestehende Schlüssel rufst du ab, indem du bei einer beliebigen App auf „Schlüssel anzeigen“ klickst. + +![Screenshot der App-Erstellung mit Alchemy](./create-app.png) + +Du kannst auch bestehende API-Schlüssel abrufen, indem du mit der Maus über „Apps“ fährst und eine auswählst. Hier kannst du den „Schlüssel anzeigen“ sowie die „App bearbeiten“, um bestimmte Domains auf die Whitelist zu setzen, mehrere Entwicklertools anzuzeigen und Analysen einzusehen. + +![Gif, das zeigt, wie ein Nutzer API-Schlüssel abruft](./pull-api-keys.gif) + +## 3. Eine Anfrage über die Befehlszeile stellen {#make-a-request-from-the-command-line} + +Interagiere mit der Ethereum-Blockchain über Alchemy mithilfe von JSON-RPC und curl. + +Für manuelle Anfragen empfehlen wir die Interaktion mit `JSON-RPC` über `POST`-Anfragen. Übergebe einfach den Header `Content-Type: application/json` und deine Anfrage als `POST`-Body mit den folgenden Feldern: + +- `jsonrpc`: Die JSON-RPC-Version – derzeit wird nur `2.0` unterstützt. +- `method`: Die ETH-API-Methode. [Siehe API-Referenz.](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) +- `params`: Eine Liste von Parametern, die an die Methode übergeben werden. +- `id`: Die ID deiner Anfrage. Wird mit der Antwort zurückgegeben, damit du verfolgen kannst, zu welcher Anfrage eine Antwort gehört. + +Hier ist ein Beispiel, das du in der Befehlszeile ausführen kannst, um den aktuellen Gaspreis abzurufen: + +```bash +curl https://eth-mainnet.alchemyapi.io/v2/demo \ +-X POST \ +-H "Content-Type: application/json" \ +-d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":73}' +``` + +_**HINWEIS:** Ersetze [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) durch deinen eigenen API-Schlüssel `https://eth-mainnet.alchemyapi.io/v2/**your-api-key`._ + +**Ergebnisse:** + +```json +{ "id": 73,"jsonrpc": "2.0","result": "0x09184e72a000" // 10000000000000 } +``` + +## 4. Richte deinen Web3-Client ein {#set-up-your-web3-client} + +**Wenn du bereits einen Client hast,** ändere die URL deines aktuellen Node Providers in eine Alchemy-URL mit deinem API-Schlüssel: `"https://eth-mainnet.alchemyapi.io/v2/your-api-key"` + +**_HINWEIS:_** Die folgenden Skripte müssen in einem **Node-Kontext** ausgeführt oder **in einer Datei gespeichert werden**, nicht über die Befehlszeile. Wenn du Node oder npm noch nicht installiert hast, sieh dir diese kurze [Einrichtungsanleitung für Macs](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs) an. + +Es gibt eine Vielzahl von [Web3-Bibliotheken](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries), die du mit Alchemy integrieren kannst. Wir empfehlen jedoch [Alchemy Web3](https://docs.alchemy.com/reference/api-overview) zu verwenden, einen Drop-in-Ersatz für web3.js, der für die nahtlose Zusammenarbeit mit Alchemy entwickelt und konfiguriert wurde. Dies bietet mehrere Vorteile, wie z. B. automatische Wiederholungsversuche und eine robuste WebSocket-Unterstützung. + +Um AlchemyWeb3.js zu installieren, **navigiere zu deinem Projektverzeichnis** und führe aus: + +**Mit Yarn:** + +``` +yarn add @alch/alchemy-web3 +``` + +**Mit NPM:** + +``` +npm install @alch/alchemy-web3 +``` + +Um mit der Knoten-Infrastruktur von Alchemy zu interagieren, führe dies in NodeJS aus oder füge es zu einer JavaScript-Datei hinzu: + +```js +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3( + "https://eth-mainnet.alchemyapi.io/v2/your-api-key" +) +``` + +## 5. Schreibe dein erstes Web3-Skript! {#write-your-first-web3-script} + +Machen wir uns nun mit ein wenig Web3-Programmierung die Hände schmutzig und schreiben ein einfaches Skript, das die neueste Blocknummer aus dem Ethereum Mainnet ausgibt. + +**1. Wenn du es noch nicht getan hast, erstelle in deinem Terminal ein neues Projektverzeichnis und wechsle mit cd hinein:** + +``` +mkdir web3-example +cd web3-example +``` + +**2. Installiere die Alchemy Web3 (oder eine andere Web3) Abhängigkeit in deinem Projekt, falls du dies noch nicht getan hast:** + +``` +npm install @alch/alchemy-web3 +``` + +**3. Erstelle eine Datei namens `index.js` und füge den folgenden Inhalt hinzu:** + +> Du solltest `demo` schlussendlich durch deinen Alchemy HTTP-API-Schlüssel ersetzen. + +```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("Die letzte Blocknummer ist " + blockNumber) +} +main() +``` + +Noch nicht mit async/await vertraut? Sieh dir diesen [Medium-Beitrag](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c) an. + +**4. Führe es mit node in deinem Terminal aus** + +``` +node index.js +``` + +**5. Du solltest jetzt die neueste Blocknummer in deiner Konsole ausgegeben sehen!** + +``` +Die letzte Blocknummer ist 11043912 +``` + +**Woo!** Glückwunsch! Du hast soeben dein erstes Web3-Skript mit Alchemy geschrieben 🎉\*\* + +Du weißt nicht, was du als Nächstes tun sollst? Versuche, deinen ersten Smart Contract bereitzustellen und versuche dich an der Solidity-Programmierung in unserem [Hello World Smart Contract Guide](https://www.alchemy.com/docs/hello-world-smart-contract), oder teste dein Dashboard-Wissen mit der [Dashboard Demo App](https://docs.alchemyapi.io/tutorials/demo-app)! + +_[Registriere dich kostenlos bei Alchemy](https://auth.alchemy.com/), sieh dir unsere [Dokumentation](https://www.alchemy.com/docs/) an und folge uns für die neuesten Nachrichten auf [Twitter](https://twitter.com/AlchemyPlatform)_. diff --git a/public/content/translations/de/developers/tutorials/guide-to-smart-contract-security-tools/index.md b/public/content/translations/de/developers/tutorials/guide-to-smart-contract-security-tools/index.md new file mode 100644 index 00000000000..f934e8e1cbe --- /dev/null +++ b/public/content/translations/de/developers/tutorials/guide-to-smart-contract-security-tools/index.md @@ -0,0 +1,102 @@ +--- +title: "Ein Leitfaden für Smart-Contract-Sicherheitstools" +description: "Ein Überblick über drei verschiedene Test- und Programmanalysetechniken" +author: "Trailofbits" +lang: de +tags: [ "solidity", "intelligente Verträge", "Sicherheit" ] +skill: intermediate +published: 2020-09-07 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis +--- + +Wir werden drei verschiedene Test- und Programmanalysetechniken verwenden: + +- **Statische Analyse mit [Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/).** Alle Pfade des Programms werden gleichzeitig durch verschiedene Programmdarstellungen (z. B. Kontrollflussgraph) angenähert und analysiert. +- **Fuzzing mit [Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/).** Der Code wird mit einer pseudozufälligen Generierung von Transaktionen ausgeführt. Der Fuzzer versucht, eine Folge von Transaktionen zu finden, die eine bestimmte Eigenschaft verletzen. +- **Symbolische Ausführung mit [Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/).** Eine formale Verifikationstechnik, die jeden Ausführungspfad in eine mathematische Formel übersetzt, anhand derer Einschränkungen überprüft werden können. + +Jede Technik hat ihre Vor- und Nachteile und ist in [bestimmten Fällen](#determining-security-properties) nützlich: + +| Technik | Werkzeug | Verwendung | Geschwindigkeit | Übersehene Fehler | Fehlalarme | +| ---------------------- | --------- | ---------------------------------------------------- | --------------- | ----------------- | ---------- | +| Statische Analyse | Slither | CLI & Skripte | Sekunden | moderat | gering | +| Fuzzing | Echidna | Solidity-Eigenschaften | Minuten | gering | keine | +| Symbolische Ausführung | Manticore | Solidity-Eigenschaften & Skripte | Stunden | keine\* | keine | + +- wenn alle Pfade ohne Zeitüberschreitung untersucht werden + +**Slither** analysiert Verträge innerhalb von Sekunden, allerdings kann eine statische Analyse zu Fehlalarmen führen und ist für komplexe Prüfungen (z. B. arithmetische Prüfungen) weniger geeignet. Führe Slither über die API aus, um per Knopfdruck auf integrierte Detektoren zuzugreifen, oder über die API für benutzerdefinierte Prüfungen. + +**Echidna** muss einige Minuten lang laufen und erzeugt nur echte Positiv-Ergebnisse. Echidna überprüft vom Benutzer bereitgestellte Sicherheitseigenschaften, die in Solidity geschrieben sind. Es können Fehler übersehen werden, da es auf einer zufälligen Untersuchung basiert. + +**Manticore** führt die "tiefgreifendste" Analyse durch. Wie Echidna verifiziert auch Manticore vom Benutzer bereitgestellte Eigenschaften. Die Ausführung dauert länger, aber es kann die Gültigkeit einer Eigenschaft beweisen und meldet keine Fehlalarme. + +## Empfohlener Arbeitsablauf {#suggested-workflow} + +Beginne mit den integrierten Detektoren von Slither, um sicherzustellen, dass jetzt und in Zukunft keine einfachen Fehler vorhanden sind. Verwende Slither, um Eigenschaften im Zusammenhang mit Vererbung, Variablenabhängigkeiten und strukturellen Problemen zu überprüfen. Wenn die Codebasis wächst, verwende Echidna, um komplexere Eigenschaften des Zustandsautomaten zu testen. Greife erneut auf Slither zurück, um benutzerdefinierte Prüfungen für Schutzmaßnahmen zu entwickeln, die in Solidity nicht verfügbar sind, wie z. B. den Schutz vor dem Überschreiben einer Funktion. Verwende schließlich Manticore, um eine gezielte Überprüfung kritischer Sicherheitseigenschaften durchzuführen, z. B. arithmetische Operationen. + +- Verwende die CLI von Slither, um häufige Probleme zu finden. +- Verwende Echidna, um übergeordnete Sicherheitseigenschaften deines Vertrags zu testen. +- Verwende Slither, um benutzerdefinierte statische Prüfungen zu schreiben. +- Verwende Manticore, wenn du eine tiefgehende Absicherung kritischer Sicherheitseigenschaften wünschst. + +**Ein Hinweis zu Unit-Tests**. Unit-Tests sind notwendig, um qualitativ hochwertige Software zu erstellen. Diese Techniken sind jedoch nicht am besten geeignet, um Sicherheitslücken zu finden. Sie werden typischerweise verwendet, um das positive Verhalten von Code zu testen (d. h. der Code funktioniert im normalen Kontext wie erwartet), während Sicherheitslücken eher in Grenzfällen auftreten, die die Entwickler nicht bedacht haben. In unserer Untersuchung von Dutzenden von Smart-Contract-Sicherheitsüberprüfungen hatte die [Unit-Test-Abdeckung keine Auswirkung auf die Anzahl oder den Schweregrad der Sicherheitslücken](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/), die wir im Code unserer Kunden fanden. + +## Bestimmung von Sicherheitseigenschaften {#determining-security-properties} + +Um deinen Code effektiv zu testen und zu verifizieren, musst du die Bereiche identifizieren, die Aufmerksamkeit erfordern. Da deine für die Sicherheit aufgewendeten Ressourcen begrenzt sind, ist es wichtig, die schwachen oder hochwertigen Teile deiner Codebasis zu bestimmen, um deinen Aufwand zu optimieren. Bedrohungsmodellierung kann dabei helfen. Ziehe Folgendes in Betracht: + +- [Rapid Risk Assessments](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (unser bevorzugter Ansatz bei Zeitmangel) +- [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.) +- [Verwendung von Assertions](https://blog.regehr.org/archives/1091) + +### Komponenten {#components} + +Wenn du weißt, was du überprüfen willst, hilft dir das auch bei der Auswahl des richtigen Tools. + +Die allgemeinen Bereiche, die für Smart Contracts häufig relevant sind, umfassen: + +- **Zustandsautomat.** Die meisten Verträge lassen sich als Zustandsautomat darstellen. Überlege zu prüfen, ob (1) kein ungültiger Zustand erreicht werden kann, (2) ein gültiger Zustand erreicht werden kann und (3) kein Zustand den Vertrag blockiert. + + - Echidna und Manticore sind die zu bevorzugenden Tools, um die Spezifikationen von Zustandsautomaten zu testen. + +- **Zugriffskontrollen.** Wenn dein System privilegierte Benutzer hat (z. B. einen Eigentümer, Controller, ...) musst du sicherstellen, dass (1) jeder Benutzer nur die autorisierten Aktionen durchführen kann und (2) kein Benutzer Aktionen eines privilegierteren Benutzers blockieren kann. + + - Slither, Echidna und Manticore können korrekte Zugriffskontrollen überprüfen. Slither kann zum Beispiel überprüfen, dass nur bei Funktionen auf der Whitelist der onlyOwner-Modifikator fehlt. Echidna und Manticore sind für komplexere Zugriffskontrollen nützlich, z. B. für eine Berechtigung, die nur erteilt wird, wenn der Vertrag einen bestimmten Zustand erreicht. + +- **Arithmetische Operationen.** Die Überprüfung der Korrektheit der arithmetischen Operationen ist entscheidend. Die durchgängige Verwendung von `SafeMath` ist ein guter Schritt, um Über- und Unterläufe zu verhindern. Dennoch musst du andere arithmetische Fehler berücksichtigen, einschließlich Rundungsfehler und Fehler, die den Vertrag blockieren. + + - Manticore ist hier die beste Wahl. Echidna kann verwendet werden, wenn die Arithmetik außerhalb des Bereichs des SMT-Solvers liegt. + +- **Korrektheit der Vererbung.** Solidity-Verträge stützen sich stark auf Mehrfachvererbung. Fehler wie eine überschattende Funktion, bei der ein `super`-Aufruf fehlt, und eine falsch interpretierte c3-Linearisierungsreihenfolge können leicht entstehen. + + - Slither ist das Tool, um die Erkennung dieser Probleme sicherzustellen. + +- **Externe Interaktionen.** Verträge interagieren miteinander, und einigen externen Verträgen sollte man nicht vertrauen. Wenn dein Vertrag zum Beispiel von externen Orakeln abhängt, bleibt er dann sicher, wenn die Hälfte der verfügbaren Orakel kompromittiert ist? + + - Manticore und Echidna sind die beste Wahl, um externe Interaktionen mit deinen Verträgen zu testen. Manticore verfügt über einen eingebauten Mechanismus, um externe Verträge zu stubben. + +- **Standardkonformität.** Ethereum-Standards (z. B. ERC20) weisen in ihrer Entwurfsgeschichte immer wieder Fehler auf. Sei dir der Einschränkungen des Standards bewusst, auf dem du aufbaust. + - Slither, Echidna und Manticore helfen dir dabei, Abweichungen von einem bestimmten Standard zu erkennen. + +### Spickzettel zur Werkzeugauswahl {#tool-selection-cheatsheet} + +| Komponente | Tools | Beispiele | +| ------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Zustandsautomat | Echidna, Manticore | | +| Zugriffskontrolle | Slither, Echidna, Manticore | [Slither-Übung 2](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md), [Echidna-Übung 2](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | +| Arithmetische Operationen | Manticore, Echidna | [Echidna-Übung 1](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md), [Manticore-Übungen 1 - 3](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | +| Korrektheit der Vererbung | Slither | [Slither-Übung 1](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | +| Externe Interaktionen | Manticore, Echidna | | +| Standardkonformität | Slither, Echidna, Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | + +Je nach deinen Zielen müssen auch andere Bereiche überprüft werden, aber diese grob umrissenen Schwerpunktbereiche sind ein guter Anfang für jedes Smart-Contract-System. + +Unsere öffentlichen Audits enthalten Beispiele für verifizierte oder getestete Eigenschaften. Lies die Abschnitte `Automated Testing and Verification` der folgenden Berichte, um dir Sicherheitseigenschaften aus der Praxis anzusehen: + +- [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/de/developers/tutorials/hello-world-smart-contract-fullstack/index.md b/public/content/translations/de/developers/tutorials/hello-world-smart-contract-fullstack/index.md new file mode 100644 index 00000000000..616ee0f18c0 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/hello-world-smart-contract-fullstack/index.md @@ -0,0 +1,1544 @@ +--- +title: "Hello World Smart-Contract für Einsteiger – Fullstack" +description: "Einführungstutorial zum Schreiben und Bereitstellen eines einfachen Smart Contracts auf Ethereum." +author: "nstrike2" +tags: + [ + "solidity", + "Hardhat", + "Alchemy", + "intelligente Verträge", + "Bereitstellung", + "Block-Explorer", + "Frontend", + "Transaktionen" + ] +skill: beginner +lang: de +published: 2021-10-25 +--- + +Dieser Leitfaden ist für Sie, wenn Sie neu in der Blockchain-Entwicklung sind und nicht wissen, wo Sie anfangen sollen oder wie Sie Smart Contracts bereitstellen und mit ihnen interagieren können. Wir werden die Erstellung und Bereitstellung eines einfachen Smart Contracts im Goerli-Testnet unter Verwendung von [MetaMask](https://metamask.io), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org) und [Alchemy](https://alchemy.com/eth) durchgehen. + +Sie benötigen ein Alchemy-Konto, um dieses Tutorial abzuschließen. [Registrieren Sie sich für ein kostenloses Konto](https://www.alchemy.com/). + +Wenn Sie zu irgendeinem Zeitpunkt Fragen haben, können Sie sich gerne im [Alchemy Discord](https://discord.gg/gWuC7zB) melden! + +## Teil 1 – Erstellen und Bereitstellen Ihres Smart Contracts mit Hardhat {#part-1} + +### Verbindung mit dem Ethereum-Netzwerk herstellen {#connect-to-the-ethereum-network} + +Es gibt viele Möglichkeiten, Anfragen an die Ethereum-Chain zu stellen. Der Einfachheit halber verwenden wir ein kostenloses Konto bei Alchemy, einer Blockchain-Entwicklerplattform und API, die es uns ermöglicht, mit der Ethereum-Chain zu kommunizieren, ohne selbst einen Node betreiben zu müssen. Alchemy verfügt auch über Entwickler-Tools für Monitoring und Analytik; wir werden diese in diesem Tutorial nutzen, um zu verstehen, was bei der Bereitstellung unseres Smart Contracts „unter der Haube“ passiert. + +### Erstellen Sie Ihre App und Ihren API-Schlüssel {#create-your-app-and-api-key} + +Sobald Sie ein Alchemy-Konto erstellt haben, können Sie einen API-Schlüssel generieren, indem Sie eine App erstellen. Damit können Sie Anfragen an das Goerli-Testnet stellen. Wenn Sie mit Testnets nicht vertraut sind, können Sie [Alchemys Leitfaden zur Auswahl eines Netzwerks](https://www.alchemy.com/docs/choosing-a-web3-network) lesen. + +Suchen Sie auf dem Alchemy-Dashboard das Dropdown-Menü **Apps** in der Navigationsleiste und klicken Sie auf **App erstellen**. + +![Hallo Welt App erstellen](./hello-world-create-app.png) + +Geben Sie Ihrer App den Namen „_Hello World_“ und schreiben Sie eine kurze Beschreibung. Wählen Sie **Staging** als Ihre Umgebung und **Goerli** als Ihr Netzwerk. + +![App-Ansicht erstellen Hallo Welt](./create-app-view-hello-world.png) + +_Hinweis: Wählen Sie unbedingt **Goerli** aus, sonst funktioniert dieses Tutorial nicht._ + +Klicken Sie auf **App erstellen**. Ihre App wird in der folgenden Tabelle angezeigt. + +### Ein Ethereum-Konto erstellen {#create-an-ethereum-account} + +Sie benötigen ein Ethereum-Konto, um Transaktionen zu senden und zu empfangen. Wir verwenden MetaMask, eine virtuelle Wallet im Browser, mit der Benutzer ihre Ethereum-Kontoadresse verwalten können. + +Sie können MetaMask [hier](https://metamask.io/download) kostenlos herunterladen und ein Konto erstellen. Wenn Sie ein Konto erstellen oder bereits eines haben, stellen Sie sicher, dass Sie oben rechts zum „Goerli Test Network“ wechseln (damit wir nicht mit echtem Geld arbeiten). + +### Schritt 4: Ether von einem Faucet hinzufügen {#step-4-add-ether-from-a-faucet} + +Um Ihren Smart Contract im Testnet bereitzustellen, benötigen Sie einige Fake-ETH. Um ETH im Goerli-Netzwerk zu erhalten, gehen Sie zu einem Goerli-Faucet und geben Sie Ihre Goerli-Kontoadresse ein. Beachten Sie, dass Goerli-Faucets in letzter Zeit etwas unzuverlässig sein können – auf der [Seite der Testnets](/developers/docs/networks/#goerli) finden Sie eine Liste mit Optionen, die Sie ausprobieren können: + +_Hinweis: Aufgrund von Netzwerküberlastung kann dies eine Weile dauern._ +`` + +### Schritt 5: Überprüfen Sie Ihr Guthaben {#step-5-check-your-balance} + +Um zu überprüfen, ob sich die ETH in Ihrer Wallet befinden, stellen wir eine [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)-Anfrage mit dem [Composer-Tool von 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). Das gibt den ETH-Betrag in unserem Wallet wieder. Um mehr zu erfahren, sehen Sie sich [Alchemys kurzes Tutorial zur Verwendung des Composer-Tools](https://youtu.be/r6sjRxBZJuU) an. + +Geben Sie Ihre MetaMask-Kontoadresse ein und klicken Sie auf **Anfrage senden**. Sie sehen eine Antwort, die wie der folgende Codeausschnitt aussieht. + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> _HINWEIS: Dieses Ergebnis ist in wei, nicht in ETH, angegeben._ Wei ist die kleinste Einheit von Ether._ + +Puh! Unser Falschgeld ist da. + +### Schritt 6: Initialisieren unseres Projekts {#step-6-initialize-our-project} + +Zunächst müssen wir einen Ordner für unser Projekt erstellen. Navigieren Sie zu Ihrer Kommandozeile und geben Sie Folgendes ein. + +``` +mkdir hello-world +cd hello-world +``` + +Da wir uns nun in unserem Projektordner befinden, verwenden wir `npm init`, um das Projekt zu initialisieren. + +> Wenn Sie npm noch nicht installiert haben, befolgen Sie [diese Anweisungen zur Installation von Node.js und npm](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm). + +Für den Zweck dieses Tutorials spielt es keine Rolle, wie Sie die Initialisierungsfragen beantworten. Hier ist, wie wir es als Referenz gemacht haben: + +``` +package name: (hello-world) +version: (1.0.0) +description: hallo Welt Smart Contract +entry point: (index.js) +test command: +git repository: +keywords: +author: +license: (ISC) + +About to write to /Users/.../.../.../hello-world/package.json: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "hallo Welt Smart Contract", + "main": "index.js", + "scripts": { + "test": "echo \"Fehler: kein Test angegeben\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +Bestätigen Sie die package.json und schon kann es losgehen! + +### Schritt 7: Hardhat herunterladen {#step-7-download-hardhat} + +Hardhat ist eine Entwicklungsumgebung zum Kompilieren, Bereitstellen, Testen und Debuggen Ihrer Ethereum-Software. Es hilft Entwicklern Smart Contracts und dApps lokal zu erstellen, bevor diese auf der Live-Chain bereitgestellt werden. + +Führen Sie in unserem `hello-world`-Projekt Folgendes aus: + +``` +npm install --save-dev hardhat +``` + +Weitere Details zu den [Installationsanweisungen](https://hardhat.org/getting-started/#overview) finden Sie auf dieser Seite. + +### Schritt 8: Hardhat-Projekt erstellen {#step-8-create-hardhat-project} + +Führen Sie im Projektordner `hello-world` Folgendes aus: + +``` +npx hardhat +``` + +Sie sollten dann eine Willkommensnachricht sehen und die Möglichkeit haben, auszuwählen, wie Sie fortfahren möchten. Wählen Sie "create an empty hardhat.config.js" (Leere hardhat.config.js erstellen) aus: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Welcome to Hardhat v2.0.11 👷‍ + +What do you want to do? … +Create a sample project +❯ Create an empty hardhat.config.js +Quit +``` + +Dadurch wird eine `hardhat.config.js`-Datei im Projekt generiert. Wir werden dies später im Tutorial verwenden, um das Setup für unser Projekt festzulegen. + +### Schritt 9: Projektordner hinzufügen {#step-9-add-project-folders} + +Um das Projekt zu organisieren, erstellen wir zwei neue Ordner. Navigieren Sie in der Kommandozeile zum Stammverzeichnis Ihres `hello-world`-Projekts und geben Sie ein: + +``` +mkdir contracts +mkdir scripts +``` + +- `contracts/` ist der Ort, an dem wir unsere `Hello-World-Smart-Contract`-Codedatei aufbewahren. +- `scripts/` ist der Ort, an dem wir Skripte zur Bereitstellung und Interaktion mit unserem Vertrag aufbewahren. + +### Schritt 10: Schreiben unseres Vertrags {#step-10-write-our-contract} + +Sie fragen sich vielleicht, wann wir anfangen, Code zu schreiben? Es ist soweit! + +Öffnen Sie das hello-world-Projekt in Ihrem bevorzugten Editor. Smart Contracts werden meistens in Solidity geschrieben, was wir verwenden werden, um unseren Smart Contract zu schreiben.‌ + +1. Navigieren Sie zum `contracts`-Ordner und erstellen Sie eine neue Datei namens `HelloWorld.sol` +2. Unten ist ein Beispiel für einen Hallo-Welt-Smart-Contract, den wir für dieses Tutorial verwenden werden. Kopieren Sie den folgenden Inhalt in die `HelloWorld.sol`-Datei. + +_Hinweis: Lesen Sie unbedingt die Kommentare, um zu verstehen, was dieser Vertrag bewirkt._ + +``` +// Gibt die Version von Solidity an, unter Verwendung von semantischer Versionierung. +// Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity >=0.7.3; + +// Definiert einen Vertrag namens `HelloWorld`. +// Ein Vertrag ist eine Sammlung von Funktionen und Daten (seinem Zustand). Nach der Bereitstellung befindet sich ein Vertrag an einer bestimmten Adresse auf der Ethereum-Blockchain. Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + //Wird ausgegeben, wenn die update-Funktion aufgerufen wird + //Smart-Contract-Ereignisse sind eine Möglichkeit für Ihren Vertrag, Ihrer App-Frontend mitzuteilen, dass auf der Blockchain etwas passiert ist. Das Frontend kann auf bestimmte Ereignisse „hören“ und bei deren Eintreten Maßnahmen ergreifen. + event UpdatedMessages(string oldStr, string newStr); + + // Deklariert eine Zustandsvariable `message` vom Typ `string`. + // Zustandsvariablen sind Variablen, deren Werte dauerhaft im Vertragsspeicher gespeichert werden. Das Schlüsselwort `public` macht Variablen von außerhalb eines Vertrags zugänglich und erstellt eine Funktion, die andere Verträge oder Clients aufrufen können, um auf den Wert zuzugreifen. + string public message; + + // Ähnlich wie in vielen klassenbasierten objektorientierten Sprachen ist ein Konstruktor eine spezielle Funktion, die nur bei der Erstellung des Vertrags ausgeführt wird. + // Konstruktoren werden verwendet, um die Daten des Vertrags zu initialisieren. Mehr erfahren:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Akzeptiert ein String-Argument `initMessage` und setzt den Wert in die Speichervariable `message` des Vertrags). + message = initMessage; + } + + // Eine öffentliche Funktion, die ein String-Argument akzeptiert und die Speichervariable `message` aktualisiert. + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +Dies ist ein einfacher Smart Contract, der bei der Erstellung eine Nachricht speichert. Er kann durch Aufrufen der `update`-Funktion aktualisiert werden. + +### Schritt 11: Verbinden Sie MetaMask und Alchemy mit Ihrem Projekt {#step-11-connect-metamask-alchemy-to-your-project} + +Wir haben eine MetaMask-Wallet und ein Alchemy-Konto erstellt und unseren Smart Contract geschrieben. Jetzt ist es an der Zeit, die drei zu verbinden. + +Jede von Ihrer Wallet gesendete Transaktion erfordert eine Signatur mit Ihrem eindeutigen privaten Schlüssel. Um unserem Programm diese Berechtigung zu erteilen, können wir unseren privaten Schlüssel sicher in einer Umgebungsdatei speichern. Wir werden hier auch einen API-Schlüssel für Alchemy speichern. + +> Um mehr über das Senden von Transaktionen zu erfahren, lesen Sie [dieses Tutorial](https://www.alchemy.com/docs/hello-world-smart-contract#step-11-connect-metamask--alchemy-to-your-project) über das Senden von Transaktionen mit web3. + +Installieren Sie zuerst das dotenv-Paket in Ihrem Projektverzeichnis: + +``` +npm install dotenv --save +``` + +Erstellen Sie dann eine `.env`-Datei im Stammverzeichnis des Projekts. Fügen Sie Ihren privaten MetaMask-Schlüssel und die HTTP-Alchemy-API-URL hinzu. + +Ihre Umgebungsdatei muss den Namen `.env` haben, sonst wird sie nicht als Umgebungsdatei erkannt. + +Nennen Sie sie nicht `process.env` oder `.env-custom` oder irgendetwas anderes. + +- Befolgen Sie [diese Anweisungen](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key), um Ihren privaten Schlüssel zu exportieren +- Siehe unten, um die HTTP Alchemy API-URL zu erhalten + +![](./get-alchemy-api-key.gif) + +Ihre `.env`-Datei sollte wie folgt aussehen: + +``` +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PRIVATE_KEY = "your-metamask-private-key" +``` + +Um diese tatsächlich mit unserem Code zu verbinden, verweisen wir in Schritt 13 in unserer `hardhat.config.js`-Datei auf diese Variablen. + +### Schritt 12: Ethers.js installieren {#step-12-install-ethersjs} + +Ethers.js ist eine Bibliothek, die es einfacher macht, mit Ethereum zu interagieren und Anfragen zu stellen, indem sie [standardmäßige JSON-RPC-Methoden](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc) mit benutzerfreundlicheren Methoden umschließt. + +Hardhat ermöglicht es uns, [Plugins](https://hardhat.org/plugins/) für zusätzliche Tools und erweiterte Funktionalität zu integrieren. Wir werden das [Ethers-Plugin](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) für die Vertragsbereitstellung nutzen. + +Geben Sie Folgendes in Ihrem Projektverzeichnis ein: + +```bash +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +### Schritt 13: Aktualisieren der hardhat.config.js {#step-13-update-hardhat-configjs} + +Wir haben bisher mehrere Abhängigkeiten und Plugins hinzugefügt. Jetzt müssen wir `hardhat.config.js` aktualisieren, damit unser Projekt alle kennt. + +Aktualisieren Sie Ihre `hardhat.config.js`, sodass sie wie folgt aussieht: + +```javascript +/** + * @type import('hardhat/config').HardhatUserConfig + */ + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") + +const { API_URL, PRIVATE_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, +} +``` + +### Schritt 14: Kompilieren unseres Vertrags {#step-14-compile-our-contract} + +Um sicherzugehen, dass so weit alles funktioniert, sollten wir unseren Vertrag erstellen. Der `compile`-Task ist einer der integrierten Hardhat-Tasks. + +Führen Sie folgenden Befehl in der Befehlszeile aus: + +```bash +npx hardhat compile +``` + +Möglicherweise erhalten Sie eine Warnung über `SPDX license identifier not provided in source file`, aber machen Sie sich darüber keine Sorgen – hoffentlich sieht alles andere gut aus! Wenn nicht, können Sie jederzeit eine Nachricht im [Alchemy-Discord](https://discord.gg/u72VCg3) schreiben. + +### Schritt 15: Schreiben unseres Bereitstellungsskripts {#step-15-write-our-deploy-script} + +Nun, da unser Vertrag geschrieben und unsere Konfigurationsdatei einsatzbereit ist, ist es an der Zeit, das Skript zur Bereitstellung des Vertrags zu schreiben. + +Navigieren Sie zum Ordner `scripts/` und erstellen Sie eine neue Datei namens `deploy.js`, und fügen Sie ihr den folgenden Inhalt hinzu: + +```javascript +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld") + + // Startet die Bereitstellung und gibt ein Promise zurück, das zu einem Vertragsobjekt aufgelöst wird + const hello_world = await HelloWorld.deploy("Hello World!") + console.log("Vertrag bereitgestellt unter Adresse:", hello_world.address) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +``` + +Hardhat erklärt in seinem [Contracts-Tutorial](https://hardhat.org/tutorial/testing-contracts.html#writing-tests) hervorragend, was jede dieser Codezeilen bewirkt; wir haben die Erklärungen hier übernommen. + +```javascript +const HelloWorld = await ethers.getContractFactory("HelloWorld") +``` + +Eine `ContractFactory` in ethers.js ist eine Abstraktion, die verwendet wird, um neue Smart Contracts bereitzustellen. `HelloWorld` ist hier also eine [Factory](https://en.wikipedia.org/wiki/Factory_\(object-oriented_programming\)) für Instanzen unseres Hallo-Welt-Vertrags. Bei der Verwendung des `hardhat-ethers`-Plugins werden `ContractFactory`- und `Contract`-Instanzen standardmäßig mit dem ersten Unterzeichner (Besitzer) verbunden. + +```javascript +const hello_world = await HelloWorld.deploy() +``` + +Der Aufruf von `deploy()` auf einer `ContractFactory` startet die Bereitstellung und gibt ein `Promise` zurück, das zu einem `Contract`-Objekt aufgelöst wird. Das ist das Objekt, das eine Methode für jede unserer Smart-Contract-Funktionen enthält. + +### Schritt 16: Unseren Vertrag bereitstellen {#step-16-deploy-our-contract} + +Nun sind wir endlich bereit, unseren Smart Contract bereitzustellen. Navigieren Sie zur Befehlszeile und führen Sie Folgendes aus: + +```bash +npx hardhat run scripts/deploy.js --network goerli +``` + +Sie sollten dann etwas sehen wie: + +```bash +Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +**Bitte speichern Sie diese Adresse**. Wir werden sie später im Tutorial verwenden. + +Wenn wir zum [Goerli-Etherscan](https://goerli.etherscan.io) gehen und nach unserer Vertragsadresse suchen, sollten wir sehen können, dass sie erfolgreich bereitgestellt wurde. Die Transaktion wird ungefähr so aussehen: + +![](./etherscan-contract.png) + +Die `From`-Adresse sollte mit Ihrer MetaMask-Kontoadresse übereinstimmen und die `To`-Adresse lautet **Vertragserstellung**. Wenn wir auf die Transaktion klicken, sehen wir unsere Vertragsadresse im `To`-Feld. + +![](./etherscan-transaction.png) + +Glückwunsch! Sie haben gerade einen Smart Contract in einem Ethereum-Testnet bereitgestellt. + +Um zu verstehen, was „unter der Haube“ vor sich geht, navigieren wir zum Explorer-Tab in unserem [Alchemy-Dashboard](https://dashboard.alchemy.com/explorer). Wenn Sie mehrere Alchemy-Apps haben, stellen Sie sicher, dass Sie nach App filtern und **Hello World** auswählen. + +![](./hello-world-explorer.png) + +Hier sehen Sie eine Handvoll JSON-RPC-Methoden, die Hardhat/Ethers für uns „unter der Haube“ ausgeführt hat, als wir die `.deploy()`-Funktion aufgerufen haben. Zwei wichtige Methoden sind hier [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), das ist die Anforderung, unseren Vertrag in die Goerli-Chain zu schreiben, und [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash), eine Anforderung zum Lesen von Informationen über unsere Transaktion anhand des Hashes. Um mehr über das Senden von Transaktionen zu erfahren, lesen Sie unser [Tutorial zum Senden von Transaktionen mit Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). + +## Teil 2: Interaktion mit Ihrem Smart Contract {#part-2-interact-with-your-smart-contract} + +Nachdem wir nun erfolgreich einen Smart Contract im Goerli-Netzwerk bereitgestellt haben, lernen wir, wie man mit ihm interagiert. + +### Eine interact.js-Datei erstellen {#create-a-interactjs-file} + +Dies ist die Datei, in die wir unser Interaktionsskript schreiben werden. Wir werden die Ethers.js-Bibliothek verwenden, die Sie zuvor in Teil 1 installiert haben. + +Erstellen Sie im Ordner `scripts/` eine neue Datei namens `interact.js` und fügen Sie den folgenden Code hinzu: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS +``` + +### Aktualisieren Sie Ihre .env-Datei {#update-your-env-file} + +Wir werden neue Umgebungsvariablen verwenden, also müssen wir sie in der `.env`-Datei definieren, die [wir zuvor erstellt haben](#step-11-connect-metamask-&-alchemy-to-your-project). + +Wir müssen eine Definition für unseren Alchemy `API_KEY` und die `CONTRACT_ADDRESS` hinzufügen, unter der Ihr Smart Contract bereitgestellt wurde. + +Ihre `.env`-Datei sollte ungefähr so aussehen: + +```bash +# .env + +API_URL = "https://eth-goerli.alchemyapi.io/v2/" +API_KEY = "" +PRIVATE_KEY = "" +CONTRACT_ADDRESS = "0x" +``` + +### Ihre Vertrags-ABI abrufen {#grab-your-contract-ABI} + +Unsere Vertrags-[ABI (Application Binary Interface)](/glossary/#abi) ist die Schnittstelle, über die die Interaktion mit unserem Smart Contract erfolgt. Hardhat generiert automatisch eine ABI und speichert sie in `HelloWorld.json`. Um die ABI zu verwenden, müssen wir den Inhalt auslesen, indem wir die folgenden Codezeilen zu unserer `interact.js`-Datei hinzufügen: + +```javascript +// interact.js +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") +``` + +Wenn Sie die ABI sehen möchten, können Sie sie auf Ihrer Konsole ausgeben: + +```javascript +console.log(JSON.stringify(contract.abi)) +``` + +Um Ihre ABI in der Konsole ausgedruckt zu sehen, navigieren Sie zu Ihrem Terminal und führen Sie aus: + +```bash +npx hardhat run scripts/interact.js +``` + +### Eine Instanz Ihres Vertrags erstellen {#create-an-instance-of-your-contract} + +Um mit unserem Vertrag zu interagieren, müssen wir eine Vertragsinstanz in unserem Code erstellen. Um dies mit Ethers.js zu tun, müssen wir mit drei Konzepten arbeiten: + +1. Provider – ein Node-Provider, der Ihnen Lese- und Schreibzugriff auf die Blockchain gibt +2. Signer – repräsentiert ein Ethereum-Konto, das Transaktionen signieren kann +3. Contract – ein Ethers.js-Objekt, das einen bestimmten onchain bereitgestellten Vertrag darstellt + +Wir verwenden die Vertrags-ABI aus dem vorherigen Schritt, um unsere Instanz des Vertrags zu erstellen: + +```javascript +// interact.js + +// Provider +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// Signer +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// Contract +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) +``` + +Erfahren Sie mehr über Provider, Signer und Contracts in der [ethers.js-Dokumentation](https://docs.ethers.io/v5/). + +### Lesen Sie die init-Nachricht {#read-the-init-message} + +Erinnern Sie sich, als wir unseren Vertrag mit `initMessage = "Hello world!"` bereitgestellt haben? Wir werden nun diese Nachricht, die in unserem Smart Contract gespeichert ist, lesen und in der Konsole ausgeben. + +In JavaScript werden asynchrone Funktionen verwendet, wenn mit Netzwerken interagiert wird. Um mehr über asynchrone Funktionen zu erfahren, [lesen Sie diesen Medium-Artikel](https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff). + +Verwenden Sie den folgenden Code, um die `message`-Funktion in unserem Smart Contract aufzurufen und die init-Nachricht zu lesen: + +```javascript +// interact.js + +// ... + +async function main() { + const message = await helloWorldContract.message() + console.log("The message is: " + message) +} +main() +``` + +Nachdem wir die Datei mit `npx hardhat run scripts/interact.js` im Terminal ausgeführt haben, sollten wir diese Antwort sehen: + +``` +Die Nachricht ist: Hallo Welt! +``` + +Glückwunsch! Sie haben gerade erfolgreich Smart-Contract-Daten von der Ethereum-Blockchain gelesen, gut gemacht! + +### Die Nachricht aktualisieren {#update-the-message} + +Anstatt nur die Nachricht zu lesen, können wir auch die in unserem Smart Contract gespeicherte Nachricht mithilfe der `update`-Funktion aktualisieren! Ziemlich cool, oder? + +Um die Nachricht zu aktualisieren, können wir die `update`-Funktion direkt auf unserem instanziierten Vertrags-Objekt aufrufen: + +```javascript +// interact.js + +// ... + +async function main() { + const message = await helloWorldContract.message() + console.log("Die Nachricht ist: " + message) + + console.log("Aktualisiere die Nachricht...") + const tx = await helloWorldContract.update("Dies ist die neue Nachricht.") + await tx.wait() +} +main() +``` + +Beachten Sie, dass wir in Zeile 11 einen Aufruf von `.wait()` auf dem zurückgegebenen Transaktionsobjekt machen. Dies stellt sicher, dass unser Skript wartet, bis die Transaktion auf der Blockchain gemined ist, bevor die Funktion beendet wird. Wenn der `.wait()`-Aufruf nicht enthalten ist, sieht das Skript möglicherweise nicht den aktualisierten `message`-Wert im Vertrag. + +### Die neue Nachricht lesen {#read-the-new-message} + +Sie sollten in der Lage sein, den [vorherigen Schritt](#read-the-init-message) zu wiederholen, um den aktualisierten `message`-Wert zu lesen. Nehmen Sie sich einen Moment Zeit und sehen Sie, ob Sie die notwendigen Änderungen vornehmen können, um diesen neuen Wert auszugeben! + +Wenn Sie einen Hinweis benötigen, hier ist, wie Ihre `interact.js`-Datei an dieser Stelle aussehen sollte: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS + +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") + +// provider - Alchemy +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// signer - Sie +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// Vertragsinstanz +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) + +async function main() { + const message = await helloWorldContract.message() + console.log("Die Nachricht ist: " + message) + + console.log("Aktualisiere die Nachricht...") + const tx = await helloWorldContract.update("Dies ist die neue Nachricht") + await tx.wait() + + const newMessage = await helloWorldContract.message() + console.log("Die neue Nachricht ist: " + newMessage) +} + +main() +``` + +Führen Sie nun einfach das Skript aus und Sie sollten die alte Nachricht, den Aktualisierungsstatus und die neue Nachricht in Ihrem Terminal ausgegeben sehen! + +`npx hardhat run scripts/interact.js --network goerli` + +``` +Die Nachricht ist: Hallo Welt! +Aktualisiere die Nachricht... +Die neue Nachricht ist: Dies ist die neue Nachricht. +``` + +Während der Ausführung dieses Skripts werden Sie vielleicht feststellen, dass der Schritt `Aktualisiere die Nachricht...` eine Weile zum Laden braucht, bevor die neue Nachricht geladen wird. Dies liegt am Mining-Prozess; wenn Sie neugierig sind, Transaktionen während des Minings zu verfolgen, besuchen Sie den [Alchemy-Mempool](https://dashboard.alchemyapi.io/mempool), um den Status einer Transaktion zu sehen. Wenn die Transaktion verworfen wird, ist es auch hilfreich, [Goerli Etherscan](https://goerli.etherscan.io) zu überprüfen und nach Ihrem Transaktions-Hash zu suchen. + +## Teil 3: Veröffentlichen Sie Ihren Smart Contract auf Etherscan {#part-3-publish-your-smart-contract-to-etherscan} + +Sie haben die ganze harte Arbeit geleistet, um Ihren Smart Contract zum Leben zu erwecken; jetzt ist es an der Zeit, ihn mit der Welt zu teilen! + +Durch die Verifizierung Ihres Smart Contracts auf Etherscan kann jeder Ihren Quellcode einsehen und mit Ihrem Smart Contract interagieren. Legen wir los! + +### Schritt 1: Generieren Sie einen API-Schlüssel in Ihrem Etherscan-Konto {#step-1-generate-an-api-key-on-your-etherscan-account} + +Ein Etherscan-API-Schlüssel ist notwendig, um zu verifizieren, dass Sie der Eigentümer des Smart Contracts sind, den Sie veröffentlichen möchten. + +Wenn Sie noch kein Etherscan-Konto haben, [registrieren Sie sich für ein Konto](https://etherscan.io/register). + +Nach dem Einloggen finden Sie Ihren Benutzernamen in der Navigationsleiste, fahren Sie mit der Maus darüber und wählen Sie die Schaltfläche **Mein Profil**. + +Auf Ihrer Profilseite sollten Sie eine seitliche Navigationsleiste sehen. Wählen Sie in der seitlichen Navigationsleiste **API-Schlüssel**. Drücken Sie als Nächstes die Schaltfläche „Hinzufügen“, um einen neuen API-Schlüssel zu erstellen, benennen Sie Ihre App **hello-world** und drücken Sie die Schaltfläche **Neuen API-Schlüssel erstellen**. + +Ihr neuer API-Schlüssel sollte in der API-Schlüssel-Tabelle erscheinen. Kopieren Sie den API-Schlüssel in Ihre Zwischenablage. + +Als Nächstes müssen wir den Etherscan-API-Schlüssel zu unserer `.env`-Datei hinzufügen. + +Nachdem Sie ihn hinzugefügt haben, sollte Ihre `.env`-Datei so aussehen: + +```javascript +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PUBLIC_KEY = "your-public-account-address" +PRIVATE_KEY = "your-private-account-address" +CONTRACT_ADDRESS = "your-contract-address" +ETHERSCAN_API_KEY = "your-etherscan-key" +``` + +### Hardhat-bereitgestellte Smart Contracts {#hardhat-deployed-smart-contracts} + +#### Installieren Sie hardhat-etherscan {#install-hardhat-etherscan} + +Das Veröffentlichen Ihres Vertrags auf Etherscan mit Hardhat ist unkompliziert. Sie müssen zuerst das `hardhat-etherscan`-Plugin installieren, um zu beginnen. `hardhat-etherscan` wird automatisch den Quellcode und die ABI des Smart Contracts auf Etherscan verifizieren. Um dies hinzuzufügen, führen Sie im `hello-world`-Verzeichnis Folgendes aus: + +```text +npm install --save-dev @nomiclabs/hardhat-etherscan +``` + +Nach der Installation fügen Sie die folgende Anweisung am Anfang Ihrer `hardhat.config.js` ein und fügen Sie die Etherscan-Konfigurationsoptionen hinzu: + +```javascript +// hardhat.config.js + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") +require("@nomiclabs/hardhat-etherscan") + +const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, + etherscan: { + // Ihr API-Schlüssel für Etherscan + // Erhalten Sie einen unter https://etherscan.io/ + apiKey: ETHERSCAN_API_KEY, + }, +} +``` + +#### Verifizieren Sie Ihren Smart Contract auf Etherscan {#verify-your-smart-contract-on-etherscan} + +Stellen Sie sicher, dass alle Dateien gespeichert und alle `.env`-Variablen korrekt konfiguriert sind. + +Führen Sie die `verify`-Aufgabe aus, indem Sie die Vertragsadresse und das Netzwerk übergeben, in dem sie bereitgestellt ist: + +```text +npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!' +``` + +Stellen Sie sicher, dass `DEPLOYED_CONTRACT_ADDRESS` die Adresse Ihres bereitgestellten Smart Contracts im Goerli-Testnet ist. Außerdem muss das letzte Argument (`'Hello World!'`) derselbe String-Wert sein, der [während des Bereitstellungsschritts in Teil 1](#write-our-deploy-script) verwendet wurde. + +Wenn alles gut geht, sehen Sie die folgende Nachricht in Ihrem Terminal: + +```text +Quellcode für Vertrag erfolgreich übermittelt +contracts/HelloWorld.sol:HelloWorld unter 0xdeployed-contract-address +zur Verifizierung auf Etherscan. Warte auf Verifizierungsergebnis... + + +Vertrag HelloWorld auf Etherscan erfolgreich verifiziert. +https://goerli.etherscan.io/address/#contracts +``` + +Glückwunsch! Ihr Smart-Contract-Code ist auf Etherscan! + +### Schauen Sie sich Ihren Smart Contract auf Etherscan an! {#check-out-your-smart-contract-on-etherscan} + +Wenn Sie dem in Ihrem Terminal bereitgestellten Link folgen, sollten Sie in der Lage sein, Ihren Smart-Contract-Code und Ihre ABI auf Etherscan veröffentlicht zu sehen! + +**Wuhuu – du hast es geschafft, Champion! Jetzt kann jeder Ihren Smart Contract aufrufen oder in ihn schreiben! Wir können es kaum erwarten zu sehen, was Sie als Nächstes bauen!** + +## Teil 4 – Integrieren Ihres Smart Contracts in das Frontend {#part-4-integrating-your-smart-contract-with-the-frontend} + +Am Ende dieses Tutorials werden Sie wissen, wie Sie: + +- Eine MetaMask-Wallet mit Ihrer Dapp verbinden +- Daten aus Ihrem Smart Contract mit der [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) API lesen +- Ethereum-Transaktionen mit MetaMask signieren + +Für diese Dapp werden wir [React](https://react.dev/) als unser Frontend-Framework verwenden; es ist jedoch wichtig zu beachten, dass wir nicht viel Zeit damit verbringen werden, dessen Grundlagen zu erläutern, da wir uns hauptsächlich darauf konzentrieren werden, Web3-Funktionalität in unser Projekt zu bringen. + +Als Voraussetzung sollten Sie ein grundlegendes Verständnis von React haben. Wenn nicht, empfehlen wir, das offizielle [Intro zu React-Tutorial](https://react.dev/learn) abzuschließen. + +### Die Starter-Dateien klonen {#clone-the-starter-files} + +Gehen Sie zunächst zum [hello-world-part-four GitHub-Repository](https://github.com/alchemyplatform/hello-world-part-four-tutorial), um die Starter-Dateien für dieses Projekt zu erhalten, und klonen Sie dieses Repository auf Ihren lokalen Rechner. + +Öffnen Sie das geklonte Repository lokal. Beachten Sie, dass es zwei Ordner enthält: `starter-files` und `completed`. + +- `starter-files` – **wir werden in diesem Verzeichnis arbeiten**, wir werden die Benutzeroberfläche mit Ihrer Ethereum-Wallet und dem Smart Contract verbinden, den wir in [Teil 3](#part-3) auf Etherscan veröffentlicht haben. +- `completed` enthält das gesamte abgeschlossene Tutorial und sollte nur als Referenz verwendet werden, wenn Sie nicht weiterkommen. + +Öffnen Sie als Nächstes Ihre Kopie von `starter-files` in Ihrem bevorzugten Code-Editor und navigieren Sie dann in den `src`-Ordner. + +Der gesamte Code, den wir schreiben werden, befindet sich im Ordner `src`. Wir werden die `HelloWorld.js`-Komponente und die `util/interact.js`-JavaScript-Dateien bearbeiten, um unserem Projekt Web3-Funktionalität zu verleihen. + +### Sehen Sie sich die Starter-Dateien an {#check-out-the-starter-files} + +Bevor wir mit dem Codieren beginnen, lassen Sie uns untersuchen, was uns in den Starter-Dateien zur Verfügung gestellt wird. + +#### Dein React-Projekt zum Laufen bringen {#get-your-react-project-running} + +Beginnen wir damit, das React-Projekt in unserem Browser auszuführen. Das Schöne an React ist, dass alle Änderungen, die wir speichern, live in unserem Browser aktualisiert werden, sobald unser Projekt im Browser läuft. + +Um das Projekt zum Laufen zu bringen, navigieren Sie zum Stammverzeichnis des `starter-files`-Ordners und führen Sie `npm install` in Ihrem Terminal aus, um die Abhängigkeiten des Projekts zu installieren: + +```bash +cd starter-files +npm install +``` + +Sobald die Installation abgeschlossen ist, führe `npm start` in deinem Terminal aus: + +```bash +npm start +``` + +Dadurch sollte [http://localhost:3000/](http://localhost:3000/) in Ihrem Browser geöffnet werden, wo Sie das Frontend für unser Projekt sehen. Es sollte aus einem Feld (einem Ort zum Aktualisieren der in Ihrem Smart Contract gespeicherten Nachricht), einer Schaltfläche „Wallet verbinden“ und einer Schaltfläche „Aktualisieren“ bestehen. + +Wenn Sie versuchen, auf eine der beiden Schaltflächen zu klicken, werden Sie feststellen, dass sie nicht funktionieren – das liegt daran, dass wir ihre Funktionalität noch programmieren müssen. + +#### Die `HelloWorld.js`-Komponente {#the-helloworld-js-component} + +Kehren wir zum `src`-Ordner in unserem Editor zurück und öffnen Sie die `HelloWorld.js`-Datei. Es ist sehr wichtig, dass wir alles in dieser Datei verstehen, da es sich um die primäre React-Komponente handelt, an der wir arbeiten werden. + +Oben in dieser Datei werden Sie feststellen, dass wir mehrere Import-Anweisungen haben, die notwendig sind, um unser Projekt zum Laufen zu bringen, einschließlich der React-Bibliothek, useEffect- und useState-Hooks, einige Elemente aus `./util/interact.js` (wir werden sie bald genauer beschreiben!) und das Alchemy-Logo. + +```javascript +// HelloWorld.js + +import React from "react" +import { useEffect, useState } from "react" +import { + helloWorldContract, + connectWallet, + updateMessage, + loadCurrentMessage, + getCurrentWalletConnected, +} from "./util/interact.js" + +import alchemylogo from "./alchemylogo.svg" +``` + +Als Nächstes haben wir unsere Zustandsvariablen, die wir nach bestimmten Ereignissen aktualisieren werden. + +```javascript +// HelloWorld.js + +//Zustandsvariablen +const [walletAddress, setWallet] = useState("") +const [status, setStatus] = useState("") +const [message, setMessage] = useState("Keine Verbindung zum Netzwerk.") +const [newMessage, setNewMessage] = useState("") +``` + +Hier ist, was jede der Variablen darstellt: + +- `walletAddress` - eine Zeichenfolge, die die Wallet-Adresse des Benutzers speichert +- `status` – ein String, der eine hilfreiche Nachricht speichert, die den Benutzer bei der Interaktion mit der Dapp anleitet +- `message` – ein String, der die aktuelle Nachricht im Smart Contract speichert +- `newMessage` – ein String, der die neue Nachricht speichert, die in den Smart Contract geschrieben wird + +Nach den Zustandsvariablen sehen Sie fünf nicht implementierte Funktionen: `useEffect` ,`addSmartContractListener`, `addWalletListener` , `connectWalletPressed` und `onUpdatePressed`. Wir erklären unten, was sie tun: + +```javascript +// HelloWorld.js + +//wird nur einmal aufgerufen +useEffect(async () => { + //TODO: implement +}, []) + +function addSmartContractListener() { + //TODO: implement +} + +function addWalletListener() { + //TODO: implement +} + +const connectWalletPressed = async () => { + //TODO: implement +} + +const onUpdatePressed = async () => { + //TODO: implement +} +``` + +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) – dies ist ein React-Hook, der aufgerufen wird, nachdem Ihre Komponente gerendert wurde. Da ihm ein leeres Array `[]` als Eigenschaft übergeben wird (siehe Zeile 4), wird er nur beim _ersten_ Rendern der Komponente aufgerufen. Hier laden wir die aktuelle Nachricht, die in unserem Smart Contract gespeichert ist, rufen unsere Smart-Contract- und Wallet-Listener auf und aktualisieren unsere Benutzeroberfläche, um anzuzeigen, ob eine Wallet bereits verbunden ist. +- `addSmartContractListener` – diese Funktion richtet einen Listener ein, der auf das `UpdatedMessages`-Ereignis unseres HelloWorld-Vertrags achtet und unsere Benutzeroberfläche aktualisiert, wenn die Nachricht in unserem Smart Contract geändert wird. +- `addWalletListener` – diese Funktion richtet einen Listener ein, der Änderungen im Zustand der MetaMask-Wallet des Benutzers erkennt, z. B. wenn der Benutzer seine Wallet trennt oder Adressen wechselt. +- `connectWalletPressed` – diese Funktion wird aufgerufen, um die MetaMask-Wallet des Benutzers mit unserer Dapp zu verbinden. +- `onUpdatePressed` – diese Funktion wird aufgerufen, wenn der Benutzer die im Smart Contract gespeicherte Nachricht aktualisieren möchte. + +Gegen Ende dieser Datei haben wir die Benutzeroberfläche unserer Komponente. + +```javascript +// HelloWorld.js + +//die Benutzeroberfläche unserer Komponente +return ( +
+ + + +

Aktuelle Nachricht:

+

{message}

+ +

Neue Nachricht:

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

{status}

+ + +
+ +
+) +``` + +Wenn Sie diesen Code sorgfältig scannen, werden Sie feststellen, wo wir unsere verschiedenen Zustandsvariablen in unserer Benutzeroberfläche verwenden: + +- In den Zeilen 6-12, wenn die Wallet des Benutzers verbunden ist (d. h. `walletAddress.length > 0`), zeigen wir eine gekürzte Version der Benutzer-`walletAddress` in der Schaltfläche mit der ID „walletButton“ an; andernfalls steht dort einfach „Wallet verbinden“. +- In Zeile 17 zeigen wir die aktuelle Nachricht an, die im Smart Contract gespeichert ist und im `message`-String erfasst wird. +- In den Zeilen 23–26 verwenden wir eine [kontrollierte Komponente](https://legacy.reactjs.org/docs/forms.html#controlled-components), um unsere `newMessage`-Zustandsvariable zu aktualisieren, wenn sich die Eingabe im Textfeld ändert. + +Zusätzlich zu unseren Zustandsvariablen sehen Sie auch, dass die Funktionen `connectWalletPressed` und `onUpdatePressed` aufgerufen werden, wenn die Schaltflächen mit den IDs `publishButton` bzw. `walletButton` geklickt werden. + +Lassen Sie uns schließlich klären, wo diese `HelloWorld.js`-Komponente hinzugefügt wird. + +Wenn Sie zur `App.js`-Datei gehen, die die Hauptkomponente in React ist und als Container für alle anderen Komponenten dient, werden Sie sehen, dass unsere `HelloWorld.js`-Komponente in Zeile 7 eingefügt wird. + +Zu guter Letzt wollen wir uns noch eine weitere für Sie bereitgestellte Datei ansehen, die `interact.js`-Datei. + +#### Die `interact.js`-Datei {#the-interact-js-file} + +Da wir uns an das [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)-Paradigma halten wollen, benötigen wir eine separate Datei, die alle unsere Funktionen zur Verwaltung der Logik, Daten und Regeln unserer Dapp enthält, und diese Funktionen dann in unser Frontend (unsere `HelloWorld.js`-Komponente) exportieren können. + +👆🏽Dies ist genau der Zweck unserer `interact.js`-Datei! + +Navigieren Sie zum `util`-Ordner in Ihrem `src`-Verzeichnis, und Sie werden feststellen, dass wir eine Datei namens `interact.js` beigefügt haben, die alle unsere Smart-Contract-Interaktions- und Wallet-Funktionen und -Variablen enthalten wird. + +```javascript +// interact.js + +//export const helloWorldContract; + +export const loadCurrentMessage = async () => {} + +export const connectWallet = async () => {} + +const getCurrentWalletConnected = async () => {} + +export const updateMessage = async (message) => {} +``` + +Sie werden am Anfang der Datei feststellen, dass wir das `helloWorldContract`-Objekt auskommentiert haben. Später in diesem Tutorial werden wir dieses Objekt entkommentieren und unseren Smart Contract in dieser Variable instanziieren, die wir dann in unsere `HelloWorld.js`-Komponente exportieren werden. + +Die vier nicht implementierten Funktionen nach unserem `helloWorldContract`-Objekt tun Folgendes: + +- `loadCurrentMessage` – diese Funktion behandelt die Logik zum Laden der aktuellen Nachricht, die im Smart Contract gespeichert ist. Es wird ein _Lese_-Aufruf an den Hallo-Welt-Smart-Contract über die [Alchemy Web3 API](https://github.com/alchemyplatform/alchemy-web3) gemacht. +- `connectWallet` – diese Funktion verbindet die MetaMask des Benutzers mit unserer Dapp. +- `getCurrentWalletConnected` – diese Funktion prüft, ob beim Laden der Seite bereits ein Ethereum-Konto mit unserer Dapp verbunden ist, und aktualisiert unsere Benutzeroberfläche entsprechend. +- `updateMessage` – diese Funktion aktualisiert die im Smart Contract gespeicherte Nachricht. Es wird ein _Schreib_-Aufruf an den Hallo-Welt-Smart-Contract gemacht, sodass die MetaMask-Wallet des Benutzers eine Ethereum-Transaktion signieren muss, um die Nachricht zu aktualisieren. + +Jetzt, da wir verstehen, womit wir arbeiten, lassen Sie uns herausfinden, wie wir aus unserem Smart Contract lesen können! + +### Schritt 3: Lesen aus Ihrem Smart Contract {#step-3-read-from-your-smart-contract} + +Um aus Ihrem Smart Contract zu lesen, müssen Sie erfolgreich Folgendes einrichten: + +- Eine API-Verbindung zur Ethereum-Chain +- Eine geladene Instanz Ihres Smart Contracts +- Eine Funktion zum Aufrufen Ihrer Smart-Contract-Funktion +- Ein Listener, der auf Aktualisierungen achtet, wenn sich die Daten, die Sie aus dem Smart Contract lesen, ändern + +Das mag nach vielen Schritten klingen, aber keine Sorge! Wir führen Sie Schritt für Schritt durch jeden einzelnen davon! :\) + +#### Eine API-Verbindung zur Ethereum-Chain herstellen {#establish-an-api-connection-to-the-ethereum-chain} + +Erinnern Sie sich, wie wir in Teil 2 dieses Tutorials unseren [Alchemy Web3-Schlüssel verwendet haben, um aus unserem Smart Contract zu lesen](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library)? Sie benötigen auch einen Alchemy Web3-Schlüssel in Ihrer Dapp, um von der Chain zu lesen. + +Wenn Sie es noch nicht haben, installieren Sie zuerst [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3), indem Sie zum Stammverzeichnis Ihrer `starter-files` navigieren und Folgendes in Ihrem Terminal ausführen: + +```text +npm install @alch/alchemy-web3 +``` + +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) ist ein Wrapper um [Web3.js](https://docs.web3js.org/) und bietet erweiterte API-Methoden und andere entscheidende Vorteile, um dir das Leben als Web3-Entwickler zu erleichtern. Es ist so konzipiert, dass es eine minimale Konfiguration erfordert, sodass du es sofort in deiner App verwenden kannst! + +Installieren Sie dann das [dotenv](https://www.npmjs.com/package/dotenv)-Paket in Ihrem Projektverzeichnis, damit wir einen sicheren Ort haben, um unseren API-Schlüssel nach dem Abrufen zu speichern. + +```text +npm install dotenv --save +``` + +Für unsere Dapp **werden wir unseren Websockets-API-Schlüssel** anstelle unseres HTTP-API-Schlüssels verwenden, da wir damit einen Listener einrichten können, der erkennt, wann sich die im Smart Contract gespeicherte Nachricht ändert. + +Sobald Sie Ihren API-Schlüssel haben, erstellen Sie eine `.env`-Datei in Ihrem Stammverzeichnis und fügen Sie Ihre Alchemy-Websockets-URL hinzu. Danach sollte Ihre `.env`-Datei so aussehen: + +```javascript +REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/ +``` + +Jetzt sind wir bereit, unseren Alchemy Web3-Endpunkt in unserer Dapp einzurichten! Kehren wir zu unserer `interact.js` zurück, die in unserem `util`-Ordner verschachtelt ist, und fügen Sie den folgenden Code am Anfang der Datei hinzu: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +//export const helloWorldContract; +``` + +Oben haben wir zuerst den Alchemy-Schlüssel aus unserer `.env`-Datei importiert und dann unseren `alchemyKey` an `createAlchemyWeb3` übergeben, um unseren Alchemy Web3-Endpunkt einzurichten. + +Mit diesem Endpunkt sind wir bereit, unseren Smart Contract zu laden! + +#### Ihren Hello World Smart Contract laden {#loading-your-hello-world-smart-contract} + +Um Ihren Hallo-Welt-Smart-Contract zu laden, benötigen Sie seine Vertragsadresse und ABI, die beide auf Etherscan zu finden sind, wenn Sie [Teil 3 dieses Tutorials abgeschlossen haben.](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan) + +#### Wie Sie Ihre Vertrags-ABI von Etherscan erhalten {#how-to-get-your-contract-abi-from-etherscan} + +Wenn Sie Teil 3 dieses Tutorials übersprungen haben, können Sie den HelloWorld-Vertrag mit der Adresse [0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code) verwenden. Seine ABI finden Sie [hier](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). + +Eine Vertrags-ABI ist notwendig, um festzulegen, welche Funktion ein Vertrag aufrufen wird, sowie um sicherzustellen, dass die Funktion Daten im erwarteten Format zurückgibt. Sobald wir unsere Vertrags-ABI kopiert haben, speichern wir sie als JSON-Datei namens `contract-abi.json` in Ihrem `src`-Verzeichnis. + +Ihre contract-abi.json sollte in Ihrem src-Ordner gespeichert sein. + +Bewaffnet mit unserer Vertragsadresse, ABI und dem Alchemy Web3-Endpunkt, können wir die [contract-Methode](https://docs.web3js.org/api/web3-eth-contract/class/Contract) verwenden, um eine Instanz unseres Smart Contracts zu laden. Importieren Sie Ihre Vertrags-ABI in die `interact.js`-Datei und fügen Sie Ihre Vertragsadresse hinzu. + +```javascript +// interact.js + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" +``` + +Wir können nun endlich unsere `helloWorldContract`-Variable entkommentieren und den Smart Contract mit unserem AlchemyWeb3-Endpunkt laden: + +```javascript +// interact.js +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +Zusammenfassend sollten die ersten 12 Zeilen Ihrer `interact.js` jetzt so aussehen: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" + +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +Jetzt, da wir unseren Vertrag geladen haben, können wir unsere `loadCurrentMessage`-Funktion implementieren! + +#### Implementierung von `loadCurrentMessage` in Ihrer `interact.js`-Datei {#implementing-loadCurrentMessage-in-your-interact-js-file} + +Diese Funktion ist super einfach. Wir werden einen einfachen asynchronen web3-Aufruf machen, um aus unserem Vertrag zu lesen. Unsere Funktion wird die im Smart Contract gespeicherte Nachricht zurückgeben: + +Aktualisieren Sie die `loadCurrentMessage` in Ihrer `interact.js`-Datei auf Folgendes: + +```javascript +// interact.js + +export const loadCurrentMessage = async () => { + const message = await helloWorldContract.methods.message().call() + return message +} +``` + +Da wir diesen Smart Contract in unserer Benutzeroberfläche anzeigen möchten, aktualisieren wir die `useEffect`-Funktion in unserer `HelloWorld.js`-Komponente auf Folgendes: + +```javascript +// HelloWorld.js + +//wird nur einmal aufgerufen +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) +}, []) +``` + +Beachten Sie, dass wir `loadCurrentMessage` nur einmal während des ersten Renderns der Komponente aufrufen wollen. Wir werden bald `addSmartContractListener` implementieren, um die Benutzeroberfläche automatisch zu aktualisieren, nachdem sich die Nachricht im Smart Contract geändert hat. + +Bevor wir uns mit unserem Listener befassen, schauen wir uns an, was wir bisher haben! Speichern Sie Ihre `HelloWorld.js`- und `interact.js`-Dateien und gehen Sie dann zu [http://localhost:3000/](http://localhost:3000/) + +Sie werden feststellen, dass die aktuelle Nachricht nicht mehr „Keine Verbindung zum Netzwerk“ lautet. Stattdessen spiegelt sie die im Smart Contract gespeicherte Nachricht wider. Klasse! + +#### Ihre Benutzeroberfläche sollte jetzt die im Smart Contract gespeicherte Nachricht widerspiegeln {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} + +Apropos Listener... + +#### Implementieren Sie `addSmartContractListener` {#implement-addsmartcontractlistener} + +Wenn Sie an die `HelloWorld.sol`-Datei zurückdenken, die wir in [Teil 1 dieser Tutorial-Reihe](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract) geschrieben haben, werden Sie sich daran erinnern, dass es ein Smart-Contract-Ereignis namens `UpdatedMessages` gibt, das ausgegeben wird, nachdem die `update`-Funktion unseres Smart Contracts aufgerufen wurde (siehe Zeilen 9 und 27): + +```javascript +// HelloWorld.sol + +// Gibt die Version von Solidity an, unter Verwendung von semantischer Versionierung. +// Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.3; + +// Definiert einen Vertrag namens `HelloWorld`. +// Ein Vertrag ist eine Sammlung von Funktionen und Daten (seinem Zustand). Nach der Bereitstellung befindet sich ein Vertrag an einer bestimmten Adresse auf der Ethereum-Blockchain. Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + //Wird ausgegeben, wenn die update-Funktion aufgerufen wird + //Smart-Contract-Ereignisse sind eine Möglichkeit für Ihren Vertrag, Ihrer App-Frontend mitzuteilen, dass auf der Blockchain etwas passiert ist. Das Frontend kann auf bestimmte Ereignisse „hören“ und bei deren Eintreten Maßnahmen ergreifen. + event UpdatedMessages(string oldStr, string newStr); + + // Deklariert eine Zustandsvariable `message` vom Typ `string`. + // Zustandsvariablen sind Variablen, deren Werte dauerhaft im Vertragsspeicher gespeichert werden. Das Schlüsselwort `public` macht Variablen von außerhalb eines Vertrags zugänglich und erstellt eine Funktion, die andere Verträge oder Clients aufrufen können, um auf den Wert zuzugreifen. + string public message; + + // Ähnlich wie in vielen klassenbasierten objektorientierten Sprachen ist ein Konstruktor eine spezielle Funktion, die nur bei der Erstellung des Vertrags ausgeführt wird. + // Konstruktoren werden verwendet, um die Daten des Vertrags zu initialisieren. Mehr erfahren:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Akzeptiert ein String-Argument `initMessage` und setzt den Wert in die Speichervariable `message` des Vertrags). + message = initMessage; + } + + // Eine öffentliche Funktion, die ein String-Argument akzeptiert und die Speichervariable `message` aktualisiert. + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +Smart-Contract-Ereignisse sind eine Möglichkeit für Ihren Vertrag, Ihrer Frontend-Anwendung mitzuteilen, dass auf der Blockchain etwas passiert ist (d. h. es gab ein _Ereignis_), die auf bestimmte Ereignisse „hören“ und bei deren Eintreten Maßnahmen ergreifen kann. + +Die `addSmartContractListener`-Funktion wird speziell auf das `UpdatedMessages`-Ereignis unseres Hallo-Welt-Smart-Contracts lauschen und unsere Benutzeroberfläche aktualisieren, um die neue Nachricht anzuzeigen. + +Ändern Sie `addSmartContractListener` in Folgendes: + +```javascript +// HelloWorld.js + +function addSmartContractListener() { + helloWorldContract.events.UpdatedMessages({}, (error, data) => { + if (error) { + setStatus("😥 " + error.message) + } else { + setMessage(data.returnValues[1]) + setNewMessage("") + setStatus("🎉 Ihre Nachricht wurde aktualisiert!") + } + }) +} +``` + +Lassen Sie uns aufschlüsseln, was passiert, wenn der Listener ein Ereignis erkennt: + +- Wenn ein Fehler auftritt, wenn das Ereignis ausgegeben wird, wird dies in der Benutzeroberfläche über unsere `status`-Zustandsvariable angezeigt. +- Andernfalls verwenden wir das zurückgegebene `data`-Objekt. Das `data.returnValues` ist ein bei Null indiziertes Array, bei dem das erste Element im Array die vorherige Nachricht und das zweite Element die aktualisierte speichert. Insgesamt werden wir bei einem erfolgreichen Ereignis unseren `message`-String auf die aktualisierte Nachricht setzen, den `newMessage`-String leeren und unsere `status`-Zustandsvariable aktualisieren, um anzuzeigen, dass eine neue Nachricht in unserem Smart Contract veröffentlicht wurde. + +Schließlich rufen wir unseren Listener in unserer `useEffect`-Funktion auf, damit er beim ersten Rendern der `HelloWorld.js`-Komponente initialisiert wird. Insgesamt sollte Ihre `useEffect`-Funktion so aussehen: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() +}, []) +``` + +Jetzt, da wir in der Lage sind, aus unserem Smart Contract zu lesen, wäre es großartig herauszufinden, wie wir auch in ihn schreiben können! Um jedoch in unsere Dapp zu schreiben, müssen wir zuerst eine Ethereum-Wallet damit verbunden haben. + +Als Nächstes werden wir uns also mit der Einrichtung unserer Ethereum-Wallet (MetaMask) befassen und sie dann mit unserer Dapp verbinden! + +### Schritt 4: Richten Sie Ihre Ethereum-Wallet ein {#step-4-set-up-your-ethereum-wallet} + +Um etwas in die Ethereum-Chain zu schreiben, müssen Benutzer Transaktionen mit den privaten Schlüsseln ihrer virtuellen Wallet signieren. Für dieses Tutorial verwenden wir [MetaMask](https://metamask.io/), eine virtuelle Wallet im Browser, die zur Verwaltung Ihrer Ethereum-Kontoadresse verwendet wird, da sie diese Transaktionssignierung für den Endbenutzer super einfach macht. + +Wenn Sie mehr darüber erfahren möchten, wie Transaktionen auf Ethereum funktionieren, sehen Sie sich [diese Seite](/developers/docs/transactions/) der Ethereum Foundation an. + +#### MetaMask herunterladen {#download-metamask} + +Sie können MetaMask [hier](https://metamask.io/download) kostenlos herunterladen und ein Konto erstellen. Wenn Sie ein Konto erstellen oder bereits eines haben, stellen Sie sicher, dass Sie oben rechts zum „Goerli Test Network“ wechseln (damit wir nicht mit echtem Geld arbeiten). + +#### Ether aus einem Faucet hinzufügen {#add-ether-from-a-faucet} + +Um eine Transaktion auf der Ethereum-Blockchain zu signieren, benötigen wir einige gefälschte Eth. Um Eth zu erhalten, können Sie zu [FaucETH](https://fauceth.komputing.org) gehen und Ihre Goerli-Kontoadresse eingeben, auf „Geld anfordern“ klicken, dann im Dropdown-Menü „Ethereum Testnet Goerli“ auswählen und schließlich erneut auf die Schaltfläche „Geld anfordern“ klicken. Kurz darauf solltest du ETH in deinem MetaMask-Konto sehen! + +#### Überprüfen Sie Ihr Guthaben {#check-your-balance} + +Um zu überprüfen, ob unser Guthaben vorhanden ist, machen wir eine [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)-Anfrage mit dem [Composer-Tool von 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). Dies gibt den Betrag an ETH in unserer Wallet zurück. Nachdem Sie die Adresse Ihres MetaMask-Kontos eingegeben und auf “Send Request” (Anforderung senden) geklickt haben, sollten Sie eine Antwort ähnlich der Folgenden erhalten: + +```text +{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} +``` + +**HINWEIS:** Dieses Ergebnis ist in Wei, nicht in ETH. Wei ist die kleinste Einheit von Ether. Die Umrechnung von Wei in ETH ist: 1 ETH = 10¹⁸ Wei. Wenn wir also 0xde0b6b3a7640000 in eine Dezimalzahl umwandeln, erhalten wir 1\*10¹⁸, was 1 ETH entspricht. + +Puh! Unser Falschgeld ist da! 🤑 + +### Schritt 5: MetaMask mit Ihrer Benutzeroberfläche verbinden {#step-5-connect-metamask-to-your-UI} + +Nachdem unsere MetaMask-Wallet nun eingerichtet ist, verbinden wir unsere Dapp damit! + +#### Die `connectWallet`-Funktion {#the-connectWallet-function} + +In unserer `interact.js`-Datei implementieren wir die `connectWallet`-Funktion, die wir dann in unserer `HelloWorld.js`-Komponente aufrufen können. + +Lassen Sie uns `connectWallet` wie folgt ändern: + +```javascript +// interact.js + +export const connectWallet = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_requestAccounts", + }) + const obj = { + status: "👆🏽 Schreiben Sie eine Nachricht in das Textfeld oben.", + address: addressArray[0], + } + return obj + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Sie müssen MetaMask, eine virtuelle Ethereum-Wallet, in Ihrem + Browser installieren. + +

+
+ ), + } + } +} +``` + +Also, was macht dieser riesige Codeblock genau? + +Zuerst prüft er, ob `window.ethereum` in Ihrem Browser aktiviert ist. + +`window.ethereum` ist eine globale API, die von MetaMask und anderen Wallet-Anbietern eingeschleust wird und es Websites ermöglicht, die Ethereum-Konten von Benutzern anzufordern. Wenn genehmigt, kann es Daten von den Blockchains lesen, mit denen der Benutzer verbunden ist, und dem Benutzer vorschlagen, Nachrichten und Transaktionen zu signieren. Weitere Informationen findest du in der [MetaMask-Dokumentation](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents)! + +Wenn `window.ethereum` _nicht_ vorhanden ist, bedeutet das, dass MetaMask nicht installiert ist. Dies führt dazu, dass ein JSON-Objekt zurückgegeben wird, bei dem die zurückgegebene `address` eine leere Zeichenfolge ist und das `status`-JSX-Objekt meldet, dass der Benutzer MetaMask installieren muss. + +Wenn `window.ethereum` jedoch vorhanden _ist_, dann wird es interessant. + +Mithilfe einer try/catch-Schleife versuchen wir, eine Verbindung zu MetaMask herzustellen, indem wir [`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts) aufrufen. Der Aufruf dieser Funktion öffnet MetaMask im Browser, woraufhin der Benutzer aufgefordert wird, seine Wallet mit deiner Dapp zu verbinden. + +- Wenn der Benutzer sich entscheidet, eine Verbindung herzustellen, gibt `method: "eth_requestAccounts"` ein Array zurück, das alle Konto-Adressen des Benutzers enthält, die mit der Dapp verbunden sind. Insgesamt gibt unsere `connectWallet`-Funktion ein JSON-Objekt zurück, das die _erste_ `address` in diesem Array (siehe Zeile 9) und eine `status`-Nachricht enthält, die den Benutzer auffordert, eine Nachricht an den Smart Contract zu schreiben. +- Wenn der Benutzer die Verbindung ablehnt, enthält das JSON-Objekt eine leere Zeichenfolge für die zurückgegebene `address` und eine `status`-Nachricht, die widerspiegelt, dass der Benutzer die Verbindung abgelehnt hat. + +Nachdem wir diese `connectWallet`-Funktion geschrieben haben, ist der nächste Schritt, sie in unserer `HelloWorld.js`-Komponente aufzurufen. + +#### Die `connectWallet`-Funktion zu Ihrer `HelloWorld.js`-UI-Komponente hinzufügen {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} + +Navigieren Sie zur `connectWalletPressed`-Funktion in `HelloWorld.js` und aktualisieren Sie sie wie folgt: + +```javascript +// HelloWorld.js + +const connectWalletPressed = async () => { + const walletResponse = await connectWallet() + setStatus(walletResponse.status) + setWallet(walletResponse.address) +} +``` + +Beachten Sie, wie der größte Teil unserer Funktionalität von unserer `HelloWorld.js`-Komponente aus der `interact.js`-Datei abstrahiert wird? Dies geschieht, damit wir dem M-V-C-Paradigma entsprechen! + +In `connectWalletPressed` machen wir einfach einen Await-Aufruf an unsere importierte `connectWallet`-Funktion, und mit ihrer Antwort aktualisieren wir unsere `status`- und `walletAddress`-Variablen über ihre State-Hooks. + +Lassen Sie uns nun beide Dateien (`HelloWorld.js` und `interact.js`) speichern und unsere bisherige Benutzeroberfläche testen. + +Öffnen Sie Ihren Browser auf der Seite [http://localhost:3000/](http://localhost:3000/) und drücken Sie die Schaltfläche „Wallet verbinden“ oben rechts auf der Seite. + +Wenn du MetaMask installiert hast, solltest du aufgefordert werden, deine Wallet mit deiner Dapp zu verbinden. Akzeptiere die Aufforderung, eine Verbindung herzustellen. + +Sie sollten sehen, dass die Wallet-Schaltfläche nun anzeigt, dass Ihre Adresse verbunden ist! Jaaaaa 🔥 + +Als Nächstes versuche, die Seite neu zu laden ... Das ist seltsam. Unsere Wallet-Schaltfläche fordert uns auf, MetaMask zu verbinden, obwohl es bereits verbunden ist ... + +Aber keine Angst! Wir können das leicht adressieren (verstanden?) indem wir `getCurrentWalletConnected` implementieren, das prüft, ob eine Adresse bereits mit unserer Dapp verbunden ist, und unsere Benutzeroberfläche entsprechend aktualisiert! + +#### Die `getCurrentWalletConnected`-Funktion {#the-getcurrentwalletconnected-function} + +Aktualisieren Sie Ihre `getCurrentWalletConnected`-Funktion in der `interact.js`-Datei wie folgt: + +```javascript +// interact.js + +export const getCurrentWalletConnected = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_accounts", + }) + if (addressArray.length > 0) { + return { + address: addressArray[0], + status: "👆🏽 Schreiben Sie eine Nachricht in das Textfeld oben.", + } + } else { + return { + address: "", + status: "🦊 Verbinden Sie sich mit MetaMask über die Schaltfläche oben rechts.", + } + } + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Sie müssen MetaMask, eine virtuelle Ethereum-Wallet, in Ihrem + Browser installieren. + +

+
+ ), + } + } +} +``` + +Dieser Code ist _sehr_ ähnlich der `connectWallet`-Funktion, die wir gerade im vorherigen Schritt geschrieben haben. + +Der Hauptunterschied besteht darin, dass wir anstelle der Methode `eth_requestAccounts`, die MetaMask für den Benutzer öffnet, um seine Wallet zu verbinden, hier die Methode `eth_accounts` aufrufen, die einfach ein Array zurückgibt, das die MetaMask-Adressen enthält, die derzeit mit unserer Dapp verbunden sind. + +Um diese Funktion in Aktion zu sehen, rufen wir sie in unserer `useEffect`-Funktion unserer `HelloWorld.js`-Komponente auf: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) +}, []) +``` + +Beachte, dass wir die Antwort unseres Aufrufs an `getCurrentWalletConnected` verwenden, um unsere `walletAddress`- und `status`-Zustandsvariablen zu aktualisieren. + +Nachdem Sie diesen Code hinzugefügt haben, versuchen wir, unser Browserfenster zu aktualisieren. + +Nett! Die Schaltfläche sollte anzeigen, dass du verbunden bist, und eine Vorschau der Adresse deiner verbundenen Wallet anzeigen – auch nach dem Aktualisieren! + +#### Implementieren Sie `addWalletListener` {#implement-addwalletlistener} + +Der letzte Schritt in der Einrichtung unserer Dapp-Wallet ist die Implementierung des Wallet-Listeners, damit unsere Benutzeroberfläche aktualisiert wird, wenn sich der Zustand unserer Wallet ändert, z. B. wenn der Benutzer die Verbindung trennt oder das Konto wechselt. + +Ändern Sie in Ihrer `HelloWorld.js`-Datei Ihre `addWalletListener`-Funktion wie folgt: + +```javascript +// HelloWorld.js + +function addWalletListener() { + if (window.ethereum) { + window.ethereum.on("accountsChanged", (accounts) => { + if (accounts.length > 0) { + setWallet(accounts[0]) + setStatus("👆🏽 Schreiben Sie eine Nachricht in das Textfeld oben.") + } else { + setWallet("") + setStatus("🦊 Verbinden Sie sich mit MetaMask über die Schaltfläche oben rechts.") + } + }) + } else { + setStatus( +

+ {" "} + 🦊 + Sie müssen MetaMask, eine virtuelle Ethereum-Wallet, in Ihrem Browser installieren. + +

+ ) + } +} +``` + +Ich wette, Sie brauchen an dieser Stelle nicht einmal mehr unsere Hilfe, um zu verstehen, was hier vor sich geht, aber aus Gründen der Vollständigkeit, lassen Sie es uns schnell aufschlüsseln: + +- Zuerst prüft unsere Funktion, ob `window.ethereum` aktiviert ist (d. h. MetaMask ist installiert). + - Wenn nicht, setzen wir einfach unsere `status`-Zustandsvariable auf eine JSX-Zeichenfolge, die den Benutzer auffordert, MetaMask zu installieren. + - Wenn es aktiviert ist, richten wir den Listener `window.ethereum.on("accountsChanged")` in Zeile 3 ein, der auf Zustandsänderungen in der MetaMask-Wallet lauscht. Dazu gehören, wenn der Benutzer ein zusätzliches Konto mit der Dapp verbindet, Konten wechselt oder ein Konto trennt. Wenn mindestens ein Konto verbunden ist, wird die Zustandsvariable `walletAddress` als das erste Konto im `accounts`-Array aktualisiert, das vom Listener zurückgegeben wird. Andernfalls wird `walletAddress` als leere Zeichenfolge festgelegt. + +Zu guter Letzt müssen wir sie in unserer `useEffect`-Funktion aufrufen: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) + + addWalletListener() +}, []) +``` + +Und das war's! Wir haben erfolgreich die gesamte Funktionalität unserer Wallet programmiert! Jetzt zu unserer letzten Aufgabe: die im Smart Contract gespeicherte Nachricht aktualisieren! + +### Schritt 6: Implementieren der `updateMessage`-Funktion {#step-6-implement-the-updateMessage-function} + +Also gut, Leute, wir sind auf der Zielgeraden! In `updateMessage` Ihrer `interact.js`-Datei werden wir Folgendes tun: + +1. Sicherstellen, dass die Nachricht, die wir in unserem Smart Contact veröffentlichen möchten, gültig ist +2. Unsere Transaktion mit MetaMask signieren +3. Diese Funktion aus unserer `HelloWorld.js`-Frontend-Komponente aufrufen + +Das wird nicht lange dauern; lassen Sie uns diese Dapp fertigstellen! + +#### Fehlerbehandlung bei der Eingabe {#input-error-handling} + +Natürlich ist es sinnvoll, am Anfang der Funktion eine Art Eingabefehlerbehandlung zu haben. + +Wir möchten, dass unsere Funktion frühzeitig zurückkehrt, wenn keine MetaMask-Erweiterung installiert ist, keine Wallet verbunden ist (d. h. die übergebene `address` ein leerer String ist) oder die `message` ein leerer String ist. Fügen wir die folgende Fehlerbehandlung zu `updateMessage` hinzu: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + if (!window.ethereum || address === null) { + return { + status: + "💡 Verbinden Sie Ihre MetaMask-Wallet, um die Nachricht auf der Blockchain zu aktualisieren.", + } + } + + if (message.trim() === "") { + return { + status: "❌ Ihre Nachricht darf kein leerer String sein.", + } + } +} +``` + +Jetzt, da wir eine ordnungsgemäße Eingabefehlerbehandlung haben, ist es Zeit, die Transaktion über MetaMask zu signieren! + +#### Unsere Transaktion signieren {#signing-our-transaction} + +Wenn Sie bereits mit traditionellen Web3-Ethereum-Transaktionen vertraut sind, wird Ihnen der Code, den wir als Nächstes schreiben, sehr bekannt vorkommen. Fügen Sie unterhalb Ihres Eingabefehlerbehandlungscodes Folgendes zu `updateMessage` hinzu: + +```javascript +// interact.js + +//Transaktionsparameter einrichten +const transactionParameters = { + to: contractAddress, // Erforderlich, außer bei der Veröffentlichung von Verträgen. + from: address, // muss mit der aktiven Adresse des Benutzers übereinstimmen. + data: helloWorldContract.methods.update(message).encodeABI(), +} + +// Transaktion signieren +try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Sehen Sie sich den Status Ihrer Transaktion auf Etherscan an! + +
+ ℹ️ Sobald die Transaktion vom Netzwerk verifiziert ist, wird die Nachricht + automatisch aktualisiert. +
+ ), + } +} catch (error) { + return { + status: "😥 " + error.message, + } +} +``` + +Schauen wir uns an, was hier passiert. Zuerst richten wir unsere Transaktionsparameter ein, wobei: + +- `to` gibt die Empfängeradresse an (unser Smart Contract) +- `from` gibt den Unterzeichner der Transaktion an, die `address`-Variable, die wir in unsere Funktion übergeben haben +- `data` enthält den Aufruf der `update`-Methode unseres Hallo-Welt-Smart-Contracts, der unsere `message`-String-Variable als Eingabe erhält + +Dann machen wir einen await-Aufruf, `window.ethereum.request`, bei dem wir MetaMask bitten, die Transaktion zu signieren. Beachten Sie, in den Zeilen 11 und 12 geben wir unsere eth-Methode `eth_sendTransaction` an und übergeben unsere `transactionParameters`. + +An diesem Punkt öffnet sich MetaMask im Browser und fordert den Benutzer auf, die Transaktion zu signieren oder abzulehnen. + +- Wenn die Transaktion erfolgreich ist, gibt die Funktion ein JSON-Objekt zurück, bei dem der `status`-JSX-String den Benutzer auffordert, Etherscan für weitere Informationen zu seiner Transaktion zu besuchen. +- Wenn die Transaktion fehlschlägt, gibt die Funktion ein JSON-Objekt zurück, bei dem der `status`-String die Fehlermeldung weitergibt. + +Insgesamt sollte unsere `updateMessage`-Funktion so aussehen: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + //Eingabefehlerbehandlung + if (!window.ethereum || address === null) { + return { + status: + "💡 Verbinden Sie Ihre MetaMask-Wallet, um die Nachricht auf der Blockchain zu aktualisieren.", + } + } + + if (message.trim() === "") { + return { + status: "❌ Ihre Nachricht darf kein leerer String sein.", + } + } + + //Transaktionsparameter einrichten + const transactionParameters = { + to: contractAddress, // Erforderlich, außer bei der Veröffentlichung von Verträgen. + from: address, // muss mit der aktiven Adresse des Benutzers übereinstimmen. + data: helloWorldContract.methods.update(message).encodeABI(), + } + + // Transaktion signieren + try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Sehen Sie sich den Status Ihrer Transaktion auf Etherscan an! + +
+ ℹ️ Sobald die Transaktion vom Netzwerk verifiziert ist, wird die Nachricht + automatisch aktualisiert. +
+ ), + } + } catch (error) { + return { + status: "😥 " + error.message, + } + } +} +``` + +Zu guter Letzt müssen wir unsere `updateMessage`-Funktion mit unserer `HelloWorld.js`-Komponente verbinden. + +#### Verbinden Sie `updateMessage` mit dem `HelloWorld.js`-Frontend {#connect-updatemessage-to-the-helloworld-js-frontend} + +Unsere `onUpdatePressed`-Funktion sollte einen await-Aufruf an die importierte `updateMessage`-Funktion machen und die `status`-Zustandsvariable ändern, um anzuzeigen, ob unsere Transaktion erfolgreich war oder fehlgeschlagen ist: + +```javascript +// HelloWorld.js + +const onUpdatePressed = async () => { + const { status } = await updateMessage(walletAddress, newMessage) + setStatus(status) +} +``` + +Es ist super sauber und einfach. Und raten Sie mal... IHRE DAPP IST FERTIG!!! + +Probieren Sie die **Aktualisieren**-Schaltfläche aus! + +### Erstellen Sie Ihre eigene benutzerdefinierte Dapp {#make-your-own-custom-dapp} + +Wooooo, Sie haben es bis zum Ende des Tutorials geschafft! Zusammenfassend haben Sie gelernt, wie Sie: + +- Eine MetaMask-Wallet mit Ihrem Dapp-Projekt verbinden +- Daten aus Ihrem Smart Contract mit der [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) API lesen +- Ethereum-Transaktionen mit MetaMask signieren + +Jetzt sind Sie voll ausgestattet, um die Fähigkeiten aus diesem Tutorial anzuwenden, um Ihr eigenes benutzerdefiniertes Dapp-Projekt zu erstellen! Wie immer, wenn Sie Fragen haben, zögern Sie nicht, uns im [Alchemy Discord](https://discord.gg/gWuC7zB) um Hilfe zu bitten. 🧙‍♂️ + +Sobald Sie dieses Tutorial abgeschlossen haben, lassen Sie uns wissen, wie Ihre Erfahrung war oder ob Sie Feedback haben, indem Sie uns auf Twitter [@alchemyplatform](https://twitter.com/AlchemyPlatform) taggen! diff --git a/public/content/translations/de/developers/tutorials/hello-world-smart-contract/index.md b/public/content/translations/de/developers/tutorials/hello-world-smart-contract/index.md index 1519d397971..07e9a34ef7d 100644 --- a/public/content/translations/de/developers/tutorials/hello-world-smart-contract/index.md +++ b/public/content/translations/de/developers/tutorials/hello-world-smart-contract/index.md @@ -1,68 +1,71 @@ --- -title: Hello World-Smart Contract für Einsteiger -description: Einführungstutorial zum Schreiben und Installieren eines einfachen Smart Contracts auf Ethereum +title: "Hello World-Smart Contract für Einsteiger" +description: "Einführungstutorial zum Schreiben und Bereitstellen eines einfachen Smart Contracts auf Ethereum." author: "elanh" tags: - - "Solidity" - - "Hardhat" - - "Alchemy" - - "Smart Contracts" - - "Erste Schritte" - - "Bereitstellung" + [ + "solidity", + "Hardhat", + "Alchemy", + "intelligente Verträge", + "Bereitstellung" + ] skill: beginner lang: de published: 2021-03-31 --- -Wenn Sie neu in der Blockchain-Entwicklung sind und nicht wissen, wo Sie anfangen sollen, oder wenn Sie einfach nur verstehen wollen, wie man Smart Contracts einsetzt und mit ihnen interagiert, ist dieser Leitfaden genau das Richtige für Sie. Wir werden die Erstellung und den Einsatz eines einfachen Smart Contracts auf dem Ropsten-Testnet unter Verwendung einer virtuellen Wallet erläutern ([MetaMask](https://metamask.io/)), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/) and [Alchemy](https://alchemyapi.io/eth) (Machen Sie sich keine Sorgen, wenn Sie noch nicht verstehen, was das alles bedeutet. Wir werden es Ihnen erklären). +Wenn Sie neu in der Blockchain-Entwicklung sind und nicht wissen, wo Sie anfangen sollen, oder wenn Sie einfach nur verstehen wollen, wie man Smart Contracts einsetzt und mit ihnen interagiert, ist dieser Leitfaden genau das Richtige für Sie. Wir werden die Erstellung und Bereitstellung eines einfachen Smart Contracts im Sepolia-Testnetz durchgehen. Dabei verwenden wir eine virtuelle Wallet ([MetaMask](https://metamask.io/)), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/) und [Alchemy](https://www.alchemy.com/eth) (keine Sorge, falls Sie noch nicht verstehen, was das alles bedeutet – wir werden es erklären). -Im zweiten Teil dieses Tutorials werden wir erläutern, wie wir mit unserem Smart Contract interagieren können, sobald er bereitgestellt wurde. Im dritten Teil erläutern wir dann, wie er auf Etherscan veröffentlicht wird. +In [Teil 2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract) dieses Tutorials gehen wir durch, wie wir mit unserem Smart Contract interagieren können, sobald er bereitgestellt wurde. In [Teil 3](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan) behandeln wir dann, wie man ihn auf Etherscan veröffentlicht. -Wenn Sie zu irgendeinem Zeitpunkt Fragen haben, dann können Sie sich im [Alchemy Discord](https://discord.gg/gWuC7zB) melden. +Wenn Sie zu irgendeinem Zeitpunkt Fragen haben, können Sie sich gerne im [Alchemy Discord](https://discord.gg/gWuC7zB) melden! -## Schritt 1: Verbindung mit dem Ethereum-Netzwerk {#step-1} +## Schritt 1: Mit dem Ethereum-Netzwerk verbinden {#step-1} -Es gibt viele Möglichkeiten, Anfragen an die Ethereum-Chain zu stellen. Der Einfachheit halber verwenden wir ein kostenloses Konto bei Alchemy, eine Blockchain-Entwicklerplattform und API, die es uns ermöglicht, mit der Ethereum-Chain zu kommunizieren, ohne dass wir unseren eigenen Node betreiben müssen. Die Plattform verfügt auch über Entwickler-Tools für die Überwachung und Analyse, die wir in diesem Tutorial nutzen werden, um zu verstehen, was unter der Haube der Smart-Contract-Bereitstellung vor sich geht. Wenn Sie noch kein Alchemy-Konto haben, [können Sie sich hier kostenlos anmelden](https://dashboard.alchemyapi.io/signup). +Es gibt viele Möglichkeiten, Anfragen an die Ethereum-Chain zu stellen. Der Einfachheit halber verwenden wir ein kostenloses Konto bei Alchemy, eine Blockchain-Entwicklerplattform und API, die es uns ermöglicht, mit der Ethereum-Chain zu kommunizieren, ohne dass wir unsere eigenen Nodes betreiben müssen. Die Plattform verfügt auch über Entwickler-Tools für die Überwachung und Analyse, die wir in diesem Tutorial nutzen werden, um zu verstehen, was bei der Bereitstellung unseres Smart Contracts „unter der Haube“ passiert. Wenn Sie noch kein Alchemy-Konto haben, [können Sie sich hier kostenlos registrieren](https://dashboard.alchemy.com/signup). -## Schritt 2: Anwendung (und den API-Schlüssel) erstellen {#step-2} +## Schritt 2: Ihre App (und den API-Schlüssel) erstellen {#step-2} -Sobald Sie ein Alchemy-Konto erstellt haben, können Sie einen API-Schlüssel generieren. Erstellen Sie dafür eine App. Darüber können wir Anfragen an das Ropsten-Testnet stellen. Wenn Sie mit Testnets nicht vertraut sind, lesen Sie [diese Seite](/developers/docs/networks/). +Sobald Sie ein Alchemy-Konto erstellt haben, können Sie einen API-Schlüssel generieren. Erstellen Sie dafür eine App. Dies ermöglicht es uns, Anfragen an das Sepolia-Testnet zu senden. Wenn Sie mit Testnets nicht vertraut sind, sehen Sie sich [diese Seite](/developers/docs/networks/) an. -1. Klicken Sie in Ihrem Alchemy-Dashboard in der Navigationsleiste unter "Apps" auf "Create App" (App erstellen), um auf die Seite "Create App" (App erstellen) zu gelangen. +1. Navigieren Sie in Ihrem Alchemy-Dashboard zur Seite „Create new app“, indem Sie in der Navigationsleiste „Select an app“ auswählen und dann auf „Create new app“ klicken. -![Hello World-App erstellen](./hello-world-create-app.png) +![Hallo Welt App erstellen](./hello-world-create-app.png) -2. Nennen Sie Ihre App "Hello World", geben Sie eine kurze Beschreibung an, wählen Sie "Staging" für die Umgebung (die für die Buchhaltung Ihrer App verwendet wird) und wählen Sie "Ropsten" als Netzwerk. +2. Geben Sie Ihrer App den Namen „Hello World“, fügen Sie eine kurze Beschreibung hinzu und wählen Sie einen Anwendungsfall aus, z. B. „Infra & Tooling“. Suchen Sie als Nächstes nach „Ethereum“ und wählen Sie das Netzwerk aus. -![Hello World-App-Ansicht erstellen](./create-app-view-hello-world.png) +![App-Ansicht erstellen Hallo Welt](./create-app-view-hello-world.png) -3. Klicken Sie auf “Create app” (App erstellen) und schon sind Sie fertig. Die App sollte in der untenstehenden Tabelle erscheinen. +3. Klicken Sie zum Fortfahren auf „Next“, dann auf „Create app“ und das war's schon! Ihre App sollte im Dropdown-Menü der Navigationsleiste erscheinen, und ein API-Schlüssel steht zum Kopieren bereit. ## Schritt 3: Ethereum-Konto (Adresse) erstellen {#step-3} -Wir benötigen ein Ethereum-Konto, um Transaktionen zu senden und zu empfangen. In diesem Tutorial verwenden wir MetaMask, eine virtuelle Wallet im Browser, mit der Sie Ihre Ethereum-Kontoadresse verwalten können. Weitere Informationen zu [Transaktionen](/developers/docs/transactions/). +Zum Versenden und Empfangen von Transaktionen benötigen Sie ein Ethereum-Konto. In diesem Tutorial verwenden wir MetaMask, eine virtuelle Wallet im Browser, mit der Sie Ihre Ethereum-Kontoadresse verwalten können. Mehr zu [Transaktionen](/developers/docs/transactions/). -Sie können MetaMask [hier](https://metamask.io/download) kostenlos herunterladen und ein Konto erstellen. Wenn Sie ein Konto erstellen oder wenn Sie bereits ein Konto haben, stellen Sie sicher, dass Sie oben rechts zum "Ropsten-Testnet" wechseln (damit wir nicht mit echtem Geld arbeiten). +Sie können MetaMask herunterladen und [hier](https://metamask.io/download) kostenlos ein Ethereum-Konto erstellen. Wenn Sie ein Konto erstellen oder bereits eines haben, wechseln Sie über das Netzwerk-Dropdown-Menü unbedingt zum Testnetz „Sepolia“ (damit wir nicht mit echtem Geld arbeiten). -![Beispiel MetaMask Ropsten](./metamask-ropsten-example.png) +Wenn Sepolia nicht aufgeführt ist, gehen Sie in das Menü, dann auf „Advanced“ und scrollen Sie nach unten, um „Show test networks“ zu aktivieren. Wählen Sie im Netzwerkauswahlmenü die Registerkarte „Custom“, um eine Liste von Testnets zu finden, und wählen Sie „Sepolia“ aus. + +![MetaMask Sepolia Beispiel](./metamask-sepolia-example.png) ## Schritt 4: Ether von einem Faucet hinzufügen {#step-4} -Um unseren Smart Contract im Testnet einzusetzen, benötigen wir einige Fake-ETH. Um ETH zu erhalten, können Sie auf den [Ropsten Faucet](https://faucet.dimensions.network/) gehen und Ihre Ropsten-Kontoadresse eingeben. Klicken Sie dann auf "Ropsten-ETH senden". Aufgrund des Netzwerkverkehrs kann es einige Zeit dauern, bis Sie Ihre Fake-ETH erhalten. Sie sollten kurz darauf ETH auf Ihrem MetaMask-Konto sehen. +Um unseren Smart Contract im Testnetz bereitzustellen, benötigen wir etwas „Fake-ETH“. Um Sepolia-ETH zu erhalten, können Sie zu den [Details des Sepolia-Netzwerks](/developers/docs/networks/#sepolia) gehen, um eine Liste verschiedener Faucets anzuzeigen. Wenn einer nicht funktioniert, versuchen Sie einen anderen, da sie manchmal leer sein können. Aufgrund des Netzwerkverkehrs kann es einige Zeit dauern, bis Sie Ihre Fake-ETH erhalten. Sie sollten kurz darauf ETH auf Ihrem MetaMask-Konto sehen! -## Schritt 5: Guthaben prüfen {#step-5} +## Schritt 5: Ihr Guthaben überprüfen {#step-5} -Um unser Guthaben zu überprüfen, müssen wir eine [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance)-Anfrage mit dem [dem Composer-Tool von 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) stellen. Dadurch wird der Betrag der ETH in unsere Wallet zurückgegeben. Nachdem Sie die Adresse Ihres MetaMask-Kontos eingegeben und auf "Anfrage senden" geklickt haben, sollten Sie eine Antwort wie diese erhalten: +Um zu überprüfen, ob unser Guthaben vorhanden ist, stellen wir eine [eth_getBalance](/developers/docs/apis/json-rpc/#eth_getbalance)-Anfrage mit dem [Composer-Tool von 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). Das gibt den ETH-Betrag in unserem Wallet wieder. Nachdem Sie die Adresse Ihres MetaMask-Kontos eingegeben und auf “Send Request” (Anforderung senden) geklickt haben, sollten Sie eine Antwort ähnlich der Folgenden erhalten: ```json { "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } ``` -> **HINWEIS:** Dieses Ergebnis ist in Wei und nicht in ETH. Wei ist die kleinste Einheit von Ether. Die Umrechnung von Wei auf ETH ist: 1 ETH = 1018 Wei. Wenn wir also 0x2B5E3AF16B1880000 in eine Dezimalzahl konvertieren, bekommen wir 5\*10¹⁸. Das entspricht 5 ETH. +> **HINWEIS:** Dieses Ergebnis ist in Wei und nicht in ETH angegeben. Wei ist die kleinste Einheit von Ether. Die Umrechnung von Wei in ETH lautet: 1 ETH = 1018 Wei. Wenn wir also 0x2B5E3AF16B1880000 in eine Dezimalzahl umwandeln, erhalten wir 5\*10¹⁸, was 5 ETH entspricht. > -> Puh! Unser Falschgeld ist da . +> Puh! Unser Fake-Geld ist da . -## Schritt 6: Projekt initialisieren {#step-6} +## Schritt 6: Unser Projekt initialisieren {#step-6} Zunächst müssen wir einen Ordner für unser Projekt erstellen. Navigieren Sie zur Befehlszeile und geben Sie Folgendes ein: @@ -71,24 +74,24 @@ mkdir hello-world cd hello-world ``` -Wir befinden uns nun in unserem Projektordner. Mit `npm init` starten wir das Projekt. Wenn Sie npm noch nicht installiert haben, befolgen Sie [diese Anleitung](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (wir brauchen auch Node.js, also laden Sie das auch herunter). +Da wir uns nun in unserem Projektordner befinden, verwenden wir `npm init`, um das Projekt zu initialisieren. Wenn Sie npm noch nicht installiert haben, folgen Sie [dieser Anleitung](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (wir benötigen auch Node.js, also laden Sie das auch herunter!). ``` npm init ``` -Es spielt keine Rolle, wie Sie die Fragen zur Installation beantworten. Wir haben es folgendermaßen gemacht: +Es spielt keine große Rolle, wie Sie die Installationsfragen beantworten. Zu Ihrer Orientierung zeigen wir Ihnen, wie wir es gemacht haben: ``` -package name: (hello-world) -version: (1.0.0) -description: hello world smart contract -entry point: (index.js) -test command: -git repository: -keywords: -author: -license: (ISC) +Paketname: (hello-world) +Version: (1.0.0) +Beschreibung: hello world smart contract +Einstiegspunkt: (index.js) +Testbefehl: +Git-Repository: +Schlüsselwörter: +Autor: +Lizenz: (ISC) About to write to /Users/.../.../.../hello-world/package.json: { @@ -104,29 +107,29 @@ About to write to /Users/.../.../.../hello-world/package.json: } ``` -Genehmigen Sie die package.json und es kann losgehen. +Bestätigen Sie die package.json und schon kann es losgehen! -## Schritt 7: [Hardhat](https://hardhat.org/getting-started/#overview){#step-7} installieren +## Schritt 7: [Hardhat](https://hardhat.org/getting-started/#overview) herunterladen {#step-7} -Hardhat ist eine Entwicklungsumgebung zum Kompilieren, Bereitstellen, Testen und Debuggen Ihrer Ethereum-Software. Es hilft Entwicklern bei der lokalen Erstellung von Smart Contracts und dApps, bevor diese auf der Live-Chain bereitgestellt werden. +Hardhat ist eine Entwicklungsumgebung zum Kompilieren, Bereitstellen, Testen und Debuggen Ihrer Ethereum-Software. Es hilft Entwicklern Smart Contracts und dApps lokal zu erstellen, bevor diese auf der Live-Chain bereitgestellt werden. -Führen Sie innerhalb unseres `hello-world`-Projekts folgenden Befehl aus: +Führen Sie in unserem `hello-world`-Projekt Folgendes aus: ``` npm install --save-dev hardhat ``` -Auf dieser Seite finden Sie weitere Informationen zur [Installationsanleitung](https://hardhat.org/getting-started/#overview). +Weitere Details zu den [Installationsanweisungen](https://hardhat.org/getting-started/#overview) finden Sie auf dieser Seite. ## Schritt 8: Hardhat-Projekt erstellen {#step-8} -Führen Sie in unserem Projektordner folgenden Befehl aus: +Führen Sie folgeden Befehl in unserem Projektordner aus: ``` npx hardhat ``` -Sie sollten eine Willkommensnachricht sehen und die Möglichkeit haben, auszuwählen, was Sie tun möchten. Wählen Sie "create an empty hardhat.config.js" (Eine leere hardhat.config.js erstellen): +Sie sollten dann eine Willkommensnachricht sehen und die Möglichkeit haben, auszuwählen, wie Sie fortfahren möchten. Wählen Sie "create an empty hardhat.config.js" (Leere hardhat.config.js erstellen) aus: ``` 888 888 888 888 888 @@ -138,114 +141,112 @@ Sie sollten eine Willkommensnachricht sehen und die Möglichkeit haben, auszuwä 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 👷‍? +👷 Willkommen bei Hardhat v2.0.11 👷‍? Was möchten Sie tun? … -Create a sample project -❯ Create an empty hardhat.config.js -Quit +Ein Beispielprojekt erstellen +❯ Eine leere hardhat.config.js erstellen +Beenden ``` -Darüber wird eine `hardhat.config.js`-Datei für uns erstellt, in der wir alle Einstellungen für unser Projekt angeben werden (in Schritt 13). +Dadurch wird eine `hardhat.config.js`-Datei für uns generiert, in der wir alle Einstellungen für unser Projekt festlegen (in Schritt 13). ## Schritt 9: Projektordner hinzufügen {#step-9} -Um unser Projekt zu organisieren, erstellen wir zwei neue Ordner. Navigieren Sie in Ihrer Befehlszeile zum Stammverzeichnis Ihres Projekts und geben Sie Folgendes ein: +Um unser Projekt zu organisieren, erstellen wir zwei neue Ordner. Navigieren Sie in der Befehlszeile zum Stammverzeichnis Ihres Projekts und geben Sie Folgendes ein: ``` mkdir contracts mkdir scripts ``` -- `contracts/` ist der Ort, an dem wir unseren hello world-Smart-Contract-Code ablegen -- `scripts/` ist der Ort, an dem wir Skripte zum Einsatz und zur Interaktion mit unserem Vertrag aufbewahren +- `contracts/` ist der Ort, an dem wir unsere `Hello-World-Smart-Contract`-Codedatei aufbewahren. +- `scripts/` ist der Ort, an dem wir Skripte zur Bereitstellung und Interaktion mit unserem Vertrag aufbewahren. -## Schritt 10: Vertrag schreiben {#step-10} +## Schritt 10: Unseren Vertrag schreiben {#step-10} -Sie fragen sich vielleicht, wann fangen wir endlich an, den Code zu schreiben? Jetzt! In Schritt 10. +Sie fragen sich vielleicht, wann wir endlich anfangen, Code zu schreiben? Nun, hier sind wir, in Schritt 10. -Öffnen Sie das hello-world-Projekt in Ihrem bevorzugten Editor (wir bevorzugen [VSCode](https://code.visualstudio.com/)). Smart Contracts werden in einer Sprache namens Solidity geschrieben, die wir verwenden werden, um unseren Smart Contract namens HelloWorld.sol zu schreiben. +Öffnen Sie das hello-world-Projekt in Ihrem bevorzugten Editor (wir mögen [VSCode](https://code.visualstudio.com/)). Smart Contracts werden in einer Sprache namens Solidity geschrieben, die wir verwenden werden, um unseren Smart Contract HelloWorld.sol zu schreiben.‌ -1. Navigieren Sie zum Ordner "contracts" (Verträge) und erstellen Sie eine neue Datei namens HelloWorld.sol. -2. Im Folgenden finden Sie ein Beispiel für einen Hello World-Smart Contract der Ethereum Foundation, den wir für dieses Tutorial verwenden. Kopieren Sie den folgenden Inhalt und fügen Sie ihn in Ihre Datei HelloWorld.sol ein. Lesen Sie die Kommentare, um zu verstehen, was dieser Vertrag bewirkt: +1. Navigieren Sie zum Ordner „contracts“ und erstellen Sie eine neue Datei mit dem Namen HelloWorld.sol +2. Unten finden Sie einen Beispiel-Smart-Contract „Hello World“ von der Ethereum Foundation, den wir für dieses Tutorial verwenden werden. Kopieren Sie den folgenden Inhalt und fügen Sie ihn in Ihre Datei HelloWorld.sol ein. Lesen Sie unbedingt die Kommentare, um zu verstehen, was dieser Vertrag bewirkt: ```solidity -// Bestimmt die Version von Solidity mit semantischer Versionierung. -// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +// Gibt die Version von Solidity an, unter Verwendung der semantischen Versionierung. +// Erfahren Sie mehr: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma pragma solidity ^0.7.0; -// Defines a contract named `HelloWorld`. -// Ein Smart contract ist eine Sammlung von Funktionen und Daten (sein Zustand). Einmal in die Blockchain integriert, befindet sich ein Contract an einer bestimmten Adresse der Ethereum-Blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +// Definiert einen Vertrag mit dem Namen `HelloWorld`. +// Ein Vertrag ist eine Sammlung von Funktionen und Daten (seinem Zustand). Nach der Bereitstellung befindet sich ein Vertrag an einer bestimmten Adresse in der Ethereum-Blockchain. Erfahren Sie mehr: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html contract HelloWorld { - // Declares a state variable `message` of type `string`. - // Zustandsvariablen sind Variablen, deren Werte dauerhaft im Vertragsspeicher hinterlegt werden. 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. + // Deklariert eine Zustandsvariable `message` vom Typ `string`. + // Zustandsvariablen sind Variablen, deren Werte dauerhaft im Vertragsspeicher gespeichert werden. Das Schlüsselwort `public` macht Variablen von außerhalb eines Vertrags zugänglich und erstellt eine Funktion, die andere Verträge oder Clients aufrufen können, um auf den Wert zuzugreifen. string public message; - // Ähnlich wie viele Klassen-basierte objektorientierte Sprachen, ist ein Konstruktor - // eine spezielle Funktion, die nur bei der Vertragserstellung ausgeführt wird. - // Konstruktoren werden verwendet, um die Vertragsdaten zu initialisieren. Erfahre mehr: https://solidity.readthedocs.io/de/v0.5.10/contracts. tml#constructors - constructor(string memory initMessage) public { - // Akzeptiert ein String Argument `initMessage` und setzt den Wert - // in die `message` Speichervariable des Contracts). + // Ähnlich wie bei vielen klassenbasierten objektorientierten Sprachen ist ein Konstruktor eine spezielle Funktion, die nur bei der Erstellung des Vertrags ausgeführt wird. + // Konstruktoren werden verwendet, um die Daten des Vertrags zu initialisieren. Erfahren Sie mehr:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Akzeptiert ein String-Argument `initMessage` und legt den Wert in der Speichervariable `message` des Vertrags fest). message = initMessage; - } + } - // Eine öffentliche Funktion, die ein String-Argument akzeptiert - // und die Speichervariable `message` aktualisiert. + // Eine öffentliche Funktion, die ein String-Argument akzeptiert und die Speichervariable `message` aktualisiert. function update(string memory newMessage) public { - message = newMessage; - } + message = newMessage; + } } ``` -Das ist ein sehr einfacher Smart Contract, der eine Nachricht bei Erstellung speichert und durch den Aufruf der `update`-Funktion aktualisiert werden kann. +Dies ist ein sehr einfacher Smart Contract, der beim Erstellen eine Nachricht speichert und durch den Aufruf der `update`-Funktion aktualisiert werden kann. ## Schritt 11: MetaMask und Alchemy mit Ihrem Projekt verbinden {#step-11} Wir haben eine MetaMask-Wallet und ein Alchemy-Konto erstellt und unseren Smart Contract geschrieben. Jetzt ist es an der Zeit, die drei zu verbinden. -Jede Transaktion, die von Ihrer virtuellen Wallet gesendet wird, erfordert eine Unterschrift mit Ihrem einzigartigen privaten Schlüssel. Um unser Programm mit dieser Berechtigung auszustatten, können wir unseren privaten Schlüssel (und den Alchemy-API-Schlüssel) sicher in einer Umgebungsdatei speichern. +Jede Transaktion, die von Ihrer virtuellen Wallet gesendet wird, benötigt eine Signatur mit ihrem eindeutigen privaten Schlüssel. Um unser Programm mit dieser Berechtigung auszustatten, können wir unseren privaten Schlüssel (und Alchemy-API-Schlüssel) in einer Umgebungsdatei sicher abspeichern. -> Weitere Informationen zum Senden von Transaktionen finden Sie in [diesem Tutorial](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) zum Senden von Transaktionen mit web3. +> Um mehr über das Senden von Transaktionen zu erfahren, sehen Sie sich [dieses Tutorial](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) über das Senden von Transaktionen mit web3 an. -Installieren Sie zunächst das dotenv-Paket in Ihrem Projektverzeichnis: +Installieren Sie zuerst das dotenv-Paket in Ihrem Projektverzeichnis: ``` npm install dotenv --save ``` -Erstellen Sie dann eine `.env`-Datei im Hauptverzeichnis unseres Projekts und fügen Sie Ihren privaten MetaMask-Schlüssel und die HTTP-API-URL von Alchemy hinzu. +Erstellen Sie dann eine `.env`-Datei im Stammverzeichnis unseres Projekts und fügen Sie Ihren privaten MetaMask-Schlüssel und Ihre HTTP-Alchemy-API-URL hinzu. -- Befolgen Sie [diese Anweisungen](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key), um Ihren privaten Schlüssel zu exportieren. -- Unten sehen Sie, wie Sie die HTTP-Alchemy API-URL erhalten. +- Befolgen Sie [diese Anweisungen](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/), um Ihren privaten Schlüssel zu exportieren +- Siehe unten, um die HTTP Alchemy API-URL zu erhalten -![Alchemy-API-Schlüssel erhalten](./get-alchemy-api-key.gif) +![Alchemy-API-Schlüssel erhalten](./get-alchemy-api-key.png) Alchemy-API-URL kopieren -Unser `.env` sollte dann so aussehen: +Ihre `.env`-Datei sollte wie folgt aussehen: ``` -API_URL = "https://eth-ropsten.alchemyapi.io/v2/your-api-key" +API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key" PRIVATE_KEY = "your-metamask-private-key" ``` -Um diese mit unserem Code zu verbinden, werden wir diese Variablen in unserer `hardhat.config.js`-Datei in Schritt 13 referenzieren. +Um diese tatsächlich mit unserem Code zu verbinden, verweisen wir in Schritt 13 in unserer `hardhat.config.js`-Datei auf diese Variablen. -Führen Sie keinen Commit für .env aus. Stellen Sie sicher, dass Sie Ihre .env-Datei niemals an andere weitergeben, denn damit würden Sie Ihre geheimen Daten weitergeben. Wenn Sie die Versionskontrolle verwenden, fügen Sie Ihre Env-Datei zu einer Datei gitignore hinzu. +Führen Sie keinen Commit für .env aus! Bitte stellen Sie sicher, dass Sie Ihre .env-Datei niemals an Dritte weitergeben oder offenlegen, da Sie dadurch Ihre Geheimnisse preisgeben. Wenn Sie eine Versionskontrolle verwenden, fügen Sie Ihre .env-Datei einer gitignore-Datei hinzu. ## Schritt 12: Ethers.js installieren {#step-12-install-ethersjs} -Ethers.js ist eine Bibliothek, die es einfacher macht, mit Ethereum zu interagieren und Anfragen zu stellen. Dafür schließt sie [Standard-JSON-RPC-Methoden](/developers/docs/apis/json-rpc/) in benutzerfreundlichere Methoden ein. +Ethers.js ist eine Bibliothek, die die Interaktion und das Stellen von Anfragen an Ethereum erleichtert, indem sie [standardmäßige JSON-RPC-Methoden](/developers/docs/apis/json-rpc/) in benutzerfreundlichere Methoden verpackt. -Hardhat macht es sehr einfach [Plug-ins](https://hardhat.org/plugins/) für zusätzliche Tools und erweiterte Funktionen zu integrieren. Wir werden das [Ethers-Plug-in](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) für die Bereitstellung von Verträgen nutzen ([Ethers.js](https://github.com/ethers-io/ethers.js/) bietet einige sehr saubere Methoden zur Bereitstellung von Verträgen). +Hardhat macht es sehr einfach, [Plugins](https://hardhat.org/plugins/) für zusätzliche Tools und erweiterte Funktionalität zu integrieren. Wir werden das [Ethers-Plugin](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) für die Vertragsbereitstellung nutzen ([Ethers.js](https://github.com/ethers-io/ethers.js/) verfügt über einige sehr saubere Methoden zur Vertragsbereitstellung). Geben Sie Folgendes in Ihrem Projektverzeichnis ein: @@ -253,13 +254,13 @@ Geben Sie Folgendes in Ihrem Projektverzeichnis ein: npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" ``` -Im nächsten Schritt benötigen wir auch Ether in unserer `hardhat.config.js`. +Im nächsten Schritt werden wir auch Ethers in unserer `hardhat.config.js` benötigen. ## Schritt 13: hardhat.config.js aktualisieren {#step-13-update-hardhatconfigjs} -Wir haben bisher mehrere Abhängigkeiten und Plug-ins hinzugefügt. Jetzt müssen wir `hardhat.config.js` aktualisieren, damit unser Projekt über alle diese Abhängigkeiten informiert wird. +Wir haben bisher mehrere Abhängigkeiten und Plugins hinzugefügt. Jetzt müssen wir `hardhat.config.js` aktualisieren, damit unser Projekt alle kennt. -Aktualisieren Sie Ihre `hardhat.config.js` so, dass sie wie folgt aussieht: +Aktualisieren Sie Ihre `hardhat.config.js`, sodass sie wie folgt aussieht: ``` require('dotenv').config(); @@ -272,10 +273,10 @@ const { API_URL, PRIVATE_KEY } = process.env; */ module.exports = { solidity: "0.7.3", - defaultNetwork: "ropsten", + defaultNetwork: "sepolia", networks: { hardhat: {}, - ropsten: { + sepolia: { url: API_URL, accounts: [`0x${PRIVATE_KEY}`] } @@ -283,9 +284,9 @@ module.exports = { } ``` -## Schritt 14: Vertragszusammenstellung {#step-14-compile-our-contracts} +## Schritt 14: Unseren Vertrag kompilieren {#step-14-compile-our-contracts} -Um sicherzugehen, dass so weit alles funktioniert, sollten wir unseren Vertrag erstellen. Die Aufgabe `compile` ist eine der integrierten Hardhat-Aufgaben. +Um sicherzugehen, dass so weit alles funktioniert, sollten wir unseren Vertrag erstellen. Der `compile`-Task ist einer der integrierten Hardhat-Tasks. Führen Sie folgenden Befehl in der Befehlszeile aus: @@ -293,19 +294,19 @@ Führen Sie folgenden Befehl in der Befehlszeile aus: npx hardhat compile ``` -Möglicherweise erhalten Sie eine Warnung über `SPDX license identifier not provided in source file` (SPDX-Lizenzkennung nicht in Quelldatei angegeben), aber darüber brauchen Sie sich keine Sorgen zu machen. Alles andere sieht hoffentlich gut aus. Falls nicht, können Sie jederzeit eine Nachricht im [Alchemy Discord](https://discord.gg/u72VCg3) hinterlassen. +Möglicherweise erhalten Sie eine Warnung `SPDX license identifier not provided in source file`, aber darüber müssen Sie sich keine Sorgen machen – hoffentlich sieht alles andere gut aus! Wenn nicht, können Sie jederzeit eine Nachricht im [Alchemy-Discord](https://discord.gg/u72VCg3) schreiben. -## Schritt 15: Bereitstellungsskript schreiben {#step-15-write-our-deploy-scripts} +## Schritt 15: Unser Bereitstellungsskript schreiben {#step-15-write-our-deploy-scripts} Nun, da unser Vertrag geschrieben und unsere Konfigurationsdatei einsatzbereit ist, ist es an der Zeit, das Skript zur Bereitstellung des Vertrags zu schreiben. -Navigieren Sie zum Ordner `scripts/` und erstellen Sie eine neue Datei namens `deploy.js`. Fügen Sie folgende Inhalte hinzu: +Navigieren Sie zum Ordner `scripts/` und erstellen Sie eine neue Datei namens `deploy.js`, und fügen Sie ihr den folgenden Inhalt hinzu: ``` async function main() { const HelloWorld = await ethers.getContractFactory("HelloWorld"); - // Start deployment, returning a promise that resolves to a contract object + // Startet die Bereitstellung und gibt ein Promise zurück, das in ein Vertragsobjekt aufgelöst wird const hello_world = await HelloWorld.deploy("Hello World!"); console.log("Contract deployed to address:", hello_world.address);} @@ -317,26 +318,26 @@ main() }); ``` -Hardhat erklärt in seinem [Vertragstutorial](https://hardhat.org/tutorial/testing-contracts.html#writing-tests) sehr gut, was die einzelnen Codezeilen bewirken. Wir haben diese Erklärungen hier übernommen. +Hardhat erklärt in seinem [Contracts-Tutorial](https://hardhat.org/tutorial/testing-contracts.html#writing-tests) hervorragend, was jede dieser Codezeilen bewirkt; wir haben die Erklärungen hier übernommen. ``` const HelloWorld = await ethers.getContractFactory("HelloWorld"); ``` -Eine `ContractFactory` in ethers.js ist eine Abstraktion, die dazu dient, neue Smart Contracts einzusetzen. So ist `HelloWorld` eine Factory for Instances von unseren hello world-Vertrag. Wenn Sie das `hardhat-ethers`-Plug-in verwenden, werden die Instanzen `ContractFactory` und `Contract` standardmäßig mit dem ersten Unterzeichner verbunden. +Eine `ContractFactory` in ethers.js ist eine Abstraktion, die zur Bereitstellung neuer Smart Contracts verwendet wird. `HelloWorld` ist hier also eine Factory für Instanzen unseres Hello-World-Vertrags. Bei Verwendung des `hardhat-ethers`-Plugins sind die Instanzen `ContractFactory` und `Contract` standardmäßig mit dem ersten Unterzeichner verbunden. ``` const hello_world = await HelloWorld.deploy(); ``` -Der Aufruf eines `deploy()` im `ContractFactory` startet die Bereitstellung und gibt einen `Promise` zurück, was zum `Contract` führt. Das ist das Objekt, das eine Methode für jede unserer Smart-Contract-Funktionen enthält. +Der Aufruf von `deploy()` auf einer `ContractFactory` startet die Bereitstellung und gibt ein `Promise` zurück, das zu einem `Contract` aufgelöst wird. Das ist das Objekt, das eine Methode für jede unserer Smart-Contract-Funktionen enthält. -## Schritt 16: Vertragsbereitstellung {#step-16-deploy-our-contract} +## Schritt 16: Unseren Vertrag bereitstellen {#step-16-deploy-our-contract} -Nun sind wir endlich bereit, unseren Smart Contract bereitzustellen. Navigieren Sie zur Befehlszeile und führen Sie folgenden Befehl aus: +Nun sind wir endlich bereit, unseren Smart Contract bereitzustellen. Navigieren Sie zur Befehlszeile und führen Sie Folgendes aus: ``` -npx hardhat run scripts/deploy.js --network ropsten +npx hardhat run scripts/deploy.js --network sepolia ``` Sie sollten dann etwas sehen wie: @@ -345,20 +346,22 @@ Sie sollten dann etwas sehen wie: Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 ``` -Wenn wir den [Ropsten etherscan](https://ropsten.etherscan.io/) aufrufen und nach unserer Vertragsadresse suchen, sollten wir sehen können, dass sie erfolgreich bereitgestellt wurde. Das Ergebnis sollte etwa wie folgt aussehen: +Wenn wir zu [Sepolia Etherscan](https://sepolia.etherscan.io/) gehen und nach unserer Vertragsadresse suchen, sollten wir sehen können, dass sie erfolgreich bereitgestellt wurde. Die Transaktion wird ungefähr so aussehen: ![Etherscan-Vertrag](./etherscan-contract.png) -Die `From`-Adresse sollte mit Ihrer MetaMask-Kontoadresse übereinstimmen und für die To-Adresse wird “Contract Creation” (Vertragserstellung) angezeigt. Doch wenn Sie die Transaktion aufrufen, erkennen Sie unsere Vertragsadresse im `To`-Feld: +Die `From`-Adresse sollte mit Ihrer MetaMask-Kontoadresse übereinstimmen und die `To`-Adresse lautet „Contract Creation“. Wenn wir jedoch auf die Transaktion klicken, sehen wir unsere Vertragsadresse im `To`-Feld: ![Etherscan-Transaktion](./etherscan-transaction.png) -Herzlichen Glückwunsch! Sie haben gerade einen Smart Contract in der Ethereum.Chain hinzugefügt 🎉. +Glückwunsch! Sie haben gerade einen Smart Contract in der Ethereum-Chain bereitgestellt 🎉 -Um zu verstehen, was im Verborgenen vor sich geht, navigieren wir zur Explorer-Registerkarte in unserem [Alchemy-Dashboard](https://dashboard.alchemyapi.io/explorer). Wenn Sie mehrere Alchemy-Anwendungen haben, stellen Sie sicher, dass Sie nach Anwendungen filtern und "Hello World" auswählen. ![Hello World-Explorer](./hello-world-explorer.png) +Um zu verstehen, was unter der Haube vor sich geht, navigieren wir zum Explorer-Tab in unserem [Alchemy-Dashboard](https://dashboard.alchemyapi.io/explorer). Wenn Sie mehrere Alchemy-Apps haben, stellen Sie sicher, dass Sie nach App filtern und „Hello World“ auswählen. +![Hello-World-Explorer](./hello-world-explorer.png) -Hier sehen Sie eine Handvoll JSON-RPC-Befehle, die Hardhat/Ethers implementiert hat, als wir die `.deploy()`-Funktion aufgerufen haben. Zwei wichtige Befehle, die wir hier vorstellen möchten, sind [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), das ist die Aufforderung unseren Vertrag in die Ropsten-Chain zu schreiben, und [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash), was eine Anfrage ist, Informationen über unsere Transaktion anhand des Hashs zu lesen (ein typisches Muster bei Transaktionen). Wenn Sie mehr über das Senden von Transaktionen erfahren möchten, lesen Sie dieses Tutorial über das [Senden von Transaktionen mit Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) +Hier sehen Sie eine Handvoll JSON-RPC-Aufrufe, die Hardhat/Ethers für uns „unter der Haube“ gemacht hat, als wir die `.deploy()`-Funktion aufgerufen haben. Zwei wichtige, die hier zu nennen sind, sind [`eth_sendRawTransaction`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-send-raw-transaction), also die Anfrage, unseren Vertrag tatsächlich in die Sepolia-Chain zu schreiben, und [`eth_getTransactionByHash`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-get-transaction-by-hash), eine Anfrage zum Lesen von Informationen über unsere Transaktion anhand des Hash (ein typisches Muster bei +Transaktionen). Um mehr über das Senden von Transaktionen zu erfahren, sehen Sie sich dieses Tutorial zum [Senden von Transaktionen mit Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) an. -Damit sind wir am Ende des ersten Teils von diesem Tutorial. Im zweiten Teil werden wir [mit unserem Smart Contract interagieren](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#part-2-interact-with-your-smart-contract). Dafür aktualisieren wir unsere anfängliche Nachricht. Im dritten Teil werden wir [unseren Smart Contract auf Etherscan veröffentlichen](https://docs.alchemyapi.io/alchemy/tutorials/hello-world-smart-contract#optional-part-3-publish-your-smart-contract-to-etherscan), damit jeder weiß, wie man mit ihm interagiert. +Das ist alles für Teil 1 dieses Tutorials. In Teil 2 werden wir tatsächlich [mit unserem Smart Contract interagieren](https://www.alchemy.com/docs/interacting-with-a-smart-contract), indem wir unsere ursprüngliche Nachricht aktualisieren, und in Teil 3 werden wir [unseren Smart Contract auf Etherscan veröffentlichen](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan), damit jeder weiß, wie man mit ihm interagiert. -**Möchten Sie mehr über Alchemy erfahren? Besuchen Sie unsere [Website](https://alchemyapi.io/eth). Sie möchten kein Update verpassen? Dann abonnieren Sie [hier](https://www.alchemyapi.io/newsletter) unseren Newsletter. Folgen Sie uns auf [Twitter](https://twitter.com/alchemyplatform) und treten Sie unserer Community in [Discord](https://discord.com/invite/u72VCg3)** bei. +**Möchten Sie mehr über Alchemy erfahren? Besuchen Sie unsere [Website](https://www.alchemy.com/eth). Möchten Sie nie ein Update verpassen? Abonnieren Sie unseren Newsletter [hier](https://www.alchemy.com/newsletter)! Treten Sie auch unserem [Discord](https://discord.gg/u72VCg3) bei.**. diff --git a/public/content/translations/de/developers/tutorials/how-to-implement-an-erc721-market/index.md b/public/content/translations/de/developers/tutorials/how-to-implement-an-erc721-market/index.md new file mode 100644 index 00000000000..ef0fa6f99d5 --- /dev/null +++ b/public/content/translations/de/developers/tutorials/how-to-implement-an-erc721-market/index.md @@ -0,0 +1,145 @@ +--- +title: Wie man einen ERC-721 Markt implementiert +description: Wie man tokenisierte Assets auf einer dezentralen Pinnwand zum Verkauf anbietet +author: "Alberto Cuesta Cañada" +tags: [ "Smart Contracts", "erc-721", "Solidity", "Token" ] +skill: intermediate +lang: de +published: 2020-03-19 +source: Hackernoon +sourceUrl: https://hackernoon.com/how-to-implement-an-erc721-market-1e1a32j9 +--- + +In diesem Artikel werde ich dir zeigen, wie du Craigslist für die Ethereum Blockchain programmieren kannst. + +Vor Ebay, Craigslist und Gumtree waren Kleinanzeigen vor allem aus Kork oder Papier gemacht. Es gab Kleinanzeigen in Schulkorridoren, Zeitungen, an Straßenbeleuchtungen und Schaufenstern. + +All das änderte sich mit dem Internet. Die Anzahl der Personen, die eine bestimmte Kleinanzeige sehen konnten, multiplizierte sich um ein Vielfaches. Damit wurden die Märkte, die sie repräsentieren, viel effizienter und skalierter auf globale Größe. Ebay ist ein massives Geschäft, dessen Ursprünge auf diese physikalischen Kleinanzeiger zurückgehen. + +Angesichts der Blockchain werden sich diese Märkte erneut ändern, lass mich dir zeigen, wie. + +## Monetisierung {#monetization} + +Das Geschäftsmodell einer öffentlichen Blockchain-Kleinanzeige muss sich von dem von Ebay und Unternehmen unterscheiden. + +Zuerst gibt es [den Dezentralisierungsaspekt](/developers/docs/web2-vs-web3/). Bestehende Plattformen müssen ihre eigenen Server unterhalten. Eine dezentrale Plattform wird von ihren Benutzern verwaltet, so dass die Kosten für den Betrieb der Kernplattform für die Plattform-Besitzer auf Null sinken. + +Dann gibt es das Front-End, die Website oder Schnittstelle, den Zugriff auf die Plattform. Hier gibt es viele Möglichkeiten. Die Besitzer der Plattform können den Zugang einschränken und jeden zwingen, ihr Interface zu verwenden und eine Gebühr zu verlangen. Die Plattformeigentümer können sich auch dazu entscheiden, den Zugang zu öffnen (Alle Macht dem Volk!) und jeden Schnittstellen zur Plattform bauen zu lassen. Oder die Eigentümer könnten sich bei jedem Ansatz für die Mitte dieser Extreme entscheiden. + +_Geschäftsführer mit mehr Vision als ich, wissen, wie man dies monetarisieren kann. Ich sehe lediglich, dass dies anders ist als der Status quo und wahrscheinlich gewinnbringend._ + +Darüber hinaus gibt es den Automatisierungs- und Zahlungsverkehrsblickwinkel. Manche Dinge können sehr [effektiv tokenisiert](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) und auf einer Kleinanzeigen-Plattform gehandelt werden. Tokenisierte Assets werden einfach in einer Blockchain übertragen. Hochkomplexe Zahlungsmethoden lassen sich in einer Blockchain einfach umsetzen. + +Ich rieche hier nur eine Geschäftsgelegenheit. Eine Kleinanzeige ohne laufende Kosten lässt sich problemlos umsetzen, wobei bei jeder Transaktion komplexe Zahlungswege enthalten sind. Ich bin mir sicher, dass jemand eine gute Idee haben wird, wofür er dies nutzen sollte. + +Ich bin nur glücklich, es zu entwickeln. Werfen wir einen Blick auf den Code. + +## Implementierung {#implementation} + +Vor einiger Zeit haben wir ein [Open-Source-Repository](https://github.com/HQ20/contracts?ref=hackernoon.com) mit Implementierungsbeispielen für Geschäftsszenarien und anderen Goodies gestartet, schau es dir bitte an. + +Der Code für dieses [Ethereum Classifieds Board](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) ist dort zu finden, bitte benutze ihn und tobe dich damit aus. Sei dir bewusst, dass der Code noch nicht geprüft wurde und du deine Sorgfaltspflicht erledigen musst, bevor du Geld in den Code steckst. + +Die Grundlagen des Boards sind nicht komplex. Alle Anzeigen im Board werden nur ein Struct mit ein paar Feldern sein: + +```solidity +struct Trade { + address poster; + uint256 item; + uint256 price; + bytes32 status; // Open, Executed, Cancelled +} +``` + +Es gibt jemanden, der die Anzeige veröffentlicht. Ein Artikel zum Verkauf. Ein Preis für diesen Artikel. Der Status des Handels, der geöffnet, ausgeführt oder aufgehoben werden kann. + +Alle diese Geschäfte werden in einer Kartierung aufbewahrt. Weil alles in Solidity ein Mapping zu sein scheint. Auch weil es bequem ist. + +```solidity +mapping(uint256 => Trade) public trades; +``` + +Die Verwendung eines Mappings bedeutet lediglich, dass wir eine ID für jedes Inserat erstellen müssen, bevor wir es veröffentlichen und wir werden die ID einer Anzeige wissen müssen, bevor wir daran arbeiten können. Es gibt mehrere Möglichkeiten, dies entweder im Smart-Contract oder im Front-end zu lösen. Bitte frage dich, ob du Pointer benötigst. + +Als nächstes stellt sich die Frage, mit welcher Währung wir uns befassen und mit welcher Währung wir die Transaktion finanzieren. + +Für die Gegenstände fordern wir lediglich, dass sie die [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com)-Schnittstelle implementieren, was eigentlich nur eine Methode ist, um reale Gegenstände in einer Blockchain darzustellen, obwohl sie am besten [mit digitalen Vermögenswerten funktioniert](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com). Wir werden unseren eigenen ERC721 Vertrag im Konstrukteur spezifizieren, was bedeutet, dass alle Vermögenswerte in unserem Kleinanzeigen-Board vorher tokenisiert werden müssen. + +Für die Zahlungen werden wir etwas Ähnliches machen. Die meisten Blockchain-Projekte definieren ihre eigene [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com) Kryptowährung. Einige andere ziehen es vor, eine Mainstream-Währung wie den DAI zu verwenden. In diesem Kleinanzeigen-Board musst du im Konstruktor über die Währung entscheiden. Einfach. + +```solidity +constructor ( + address _currencyTokenAddress, address _itemTokenAddress +) public { + currencyToken = IERC20(_currencyTokenAddress); + itemToken = IERC721(_itemTokenAddress); + tradeCounter = 0; +} +``` + +Wir haben es fast geschafft. Wir haben Werbung, Artikel für den Handel und eine Währung für Zahlungen. Eine Anzeige zu machen bedeutet, einen Gegenstand in das Treuhandkontol zu legen und zu zeigen, dass du ihn nicht zweimal gepostet hast, möglicherweise in einem anderen Board. + +Der folgende Code tut genau das. Legt den Gegenstand in escrow, erstellt die Werbung, macht ein wenig den Haushalt. + +```solidity +function openTrade(uint256 _item, uint256 _price) + public +{ + itemToken.transferFrom(msg.sender, address(this), _item); + trades[tradeCounter] = Trade({ + poster: msg.sender, + item: _item, + price: _price, + status: "Open" + }); + tradeCounter += 1; + emit TradeStatusChange(tradeCounter - 1, "Open"); +} +``` + +Den Handel zu akzeptieren bedeutet, wähle eine Anzeige (Handel), zahle den Preis, erhalte den Artikel. Der untenstehende Code ruft einen Handel auf. Überprüft, ob er verfügbar ist. Bezahlt den Artikel. Erhält den Artikel. Aktualisiert die Anzeige. + +```solidity +function executeTrade(uint256 _trade) + public +{ + Trade memory trade = trades[_trade]; + require(trade.status == "Open", "Trade is not Open."); + currencyToken.transferFrom(msg.sender, trade.poster, trade.price); + itemToken.transferFrom(address(this), msg.sender, trade.item); + trades[_trade].status = "Executed"; + emit TradeStatusChange(_trade, "Executed"); +} +``` + +Schließlich haben wir die Möglichkeit für Verkäufer, von einem Handel zurückzutreten, bevor ein Käufer ihn annimmt. In einigen Modellen würden Anzeigen statt dessen eine Zeitspanne lang live sein, bevor sie ablaufen. Deine Wahl, abhängig vom Design Ihres Marktes. + +Der Code ist sehr ähnlich wie bei der Handelsausführung nur dass keine Währung wechselt wird und der Artikel zurück zum Verkäufer geht. + +```solidity +function cancelTrade(uint256 _trade) + public +{ + Trade memory trade = trades[_trade]; + require( + msg.sender == trade.poster, + "Der Handel kann nur vom Ersteller storniert werden." + ); + require(trade.status == "Open", "Der Handel ist nicht offen."); + itemToken.transferFrom(address(this), trade.poster, trade.item); + trades[_trade].status = "Cancelled"; + emit TradeStatusChange(_trade, "Cancelled"); +} +``` + +Das wars. Du hast es bis zum Ende der Umsetzung geschafft. Es ist ziemlich überraschend, wie kompakt einige Geschäftskonzepte sind, wenn sie im Code ausgedrückt werden, und das ist einer dieser Fälle. Sieh dir den vollständigen Vertrag [in unserem Repo](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol) an. + +## Fazit {#conclusion} + +Kleinanzeigen sind eine gemeinsame Marktkonfiguration, die massiv mit dem Internet skaliert wurde und zu einem sehr beliebten Geschäftsmodell mit ein paar monopolistischen Gewinnern geworden ist. + +Kleinanzeigen sind auch ein einfaches Werkzeug zum Nachahmen in einer Blockchain-Umgebung mit sehr spezifischen Merkmalen, die eine Herausforderung für die bestehenden Marktführer ermöglichen. + +In diesem Artikel habe ich den Versuch unternommen, die Geschäftswirklichkeit eines Kleinanzeigen-Business mit der technologischen Umsetzung zu verbinden. Dieses Wissen sollte dir helfen, eine Vision und einen Fahrplan für die Umsetzung zu erstellen, wenn du über die richtigen Fähigkeiten verfügst. + +Wie immer gilt: Wenn du etwas Lustiges bauen willst und einen Ratschlag gebrauchen könntest, [schreib mir einfach eine Nachricht](https://albertocuesta.es/)! Ich helfe immer gerne.