Durch die Befolgung dieser Praktiken können Entwickler den Gasverbrauch in Smart Contracts reduzieren, Transaktionskosten senken und effizientere und benutzerfreundlichere Anwendungen erstellen.
Gasgebühren auf dem Ethereum-Mainnet waren schon immer ein großes Problem, insbesondere während Zeiten von Netzwerküberlastung. Zu Spitzenzeiten müssen Benutzer oft extrem hohe Transaktionsgebühren zahlen. Daher ist die Optimierung der Gas-Kosten während der Entwicklung von Smart Contracts entscheidend. Die Gasoptimierung kann nicht nur die Transaktionskosten effektiv reduzieren, sondern auch die Transaktionseffizienz verbessern und den Benutzern ein wirtschaftlicheres und effizienteres Blockchain-Erlebnis bieten.
In diesem Artikel wird der Gasgebührenmechanismus der Ethereum Virtual Machine (EVM), die Kernkonzepte im Zusammenhang mit der Optimierung von Gasgebühren und bewährte Verfahren zur Optimierung von Gasgebühren bei der Entwicklung von Smart Contracts erläutert. Es wird gehofft, dass dieser Inhalt Entwickler inspiriert und unterstützt, während er auch gewöhnlichen Benutzern hilft, das Funktionieren des EVM-Gasgebührensystems besser zu verstehen und so die Herausforderungen im Blockchain-Ökosystem anzugehen.
In EVM-kompatiblen Netzwerken bezieht sich "Gas" auf die Einheit, die zur Messung der Rechenleistung erforderlich ist, um bestimmte Operationen auszuführen.
Die folgende Abbildung veranschaulicht die Struktur der EVM. In der Abbildung wird der Gasverbrauch in drei Teile aufgeteilt: Ausführung von Operationen, externe Nachrichtenaufrufe sowie Lese- und Schreibvorgänge des Arbeitsspeichers und des Speichers.
Quelle: Offizielle Ethereum-Website[1]
Seit der Aktivierung von EIP-1559 (London Hard Fork) werden die Gasgebühren mit folgender Formel berechnet:
Gasgebühr = verwendete Gaseinheiten * (Grundgebühr + Prioritätsgebühr)
Die Grundgebühr wird verbrannt, während die Prioritätsgebühr als Anreiz dient, die Validatoren dazu zu ermutigen, die Transaktion in die Blockchain aufzunehmen. Wenn Sie eine Transaktion mit einer höheren Prioritätsgebühr senden, erhöht sich die Wahrscheinlichkeit, dass die Transaktion im nächsten Block enthalten ist. Dies ähnelt einem „Trinkgeld“, das von Benutzern an die Validatoren gezahlt wird.
Beim Kompilieren eines Smart Contracts mit Solidity wird der Vertrag in eine Reihe von "Betriebscodes" oder Opcodes umgewandelt.
Jeder Opcode (wie das Erstellen eines Vertrags, das Durchführen von Nachrichtenaufrufen, der Zugriff auf den Kontospeicher und das Ausführen von Operationen auf der virtuellen Maschine) hat eine zugehörige Gasverbrauchskosten, die im Ethereum Yellow Paper[2] dokumentiert sind.
Nach mehreren EIP-Änderungen wurden die Gasgebühren einiger Opcodes angepasst, die von den Werten im Yellow Paper abweichen können. Für detaillierte Informationen zu den neuesten Kosten von Opcodes, siehe diese Quelle[3].
Das Kernkonzept der Gasoptimierung besteht darin, kosteneffiziente Operationen auf der EVM-Blockchain zu priorisieren und Operationen zu vermeiden, die hohe Gas kosten verursachen.
Im EVM sind die folgenden Operationen vergleichsweise kostengünstig:
Zu den kostspieligen Operationen gehören:
Basierend auf den oben genannten Grundkonzepten haben wir eine Liste bewährter Verfahren zur Optimierung der Gasgebühr für die Entwicklergemeinschaft zusammengestellt. Durch die Befolgung dieser Praktiken können Entwickler den Gasverbrauch von Smart Contracts reduzieren, Transaktionskosten senken und effizientere und benutzerfreundlichere Anwendungen erstellen.
In Solidity, Speicher ist eine begrenzte Ressource, und sein Gasverbrauch ist deutlich höher als der von Memory. Jedes Mal, wenn ein Smart Contract aus dem Speicher liest oder in ihn schreibt, entsteht ein hoher Gasverbrauch.
Gemäß der Definition im Ethereum Yellow Paper ist der Speicheraufwand für Speicheroperationen mehr als 100-mal höher als der Speicheraufwand für Arbeitsspeicheroperationen. Zum Beispiel kosten Opcodes wie sload und sstore im besten Fall mindestens 100 Gas-Einheiten, während Arbeitsspeicheroperationen wie mload und mstore nur 3 Gas-Einheiten verbrauchen.
Methoden zur Begrenzung des Speicherverbrauchs umfassen:
Die Anzahl der Speicherplätze, die in einem Smart Contract verwendet werden, und die Art und Weise, wie Entwickler Daten darstellen, können den Gasverbrauch erheblich beeinflussen.
Der Solidity-Compiler packt während des Kompilierungsprozesses aufeinanderfolgende Speichervariablen unter Verwendung von 32-Byte-Speicherslots als Grundlage für die Variablenspeicherung. Die Variablenverpackung bezieht sich auf die Praxis, Variablen so anzuordnen, dass mehrere Variablen in einen einzigen Speicherslot passen.
Links befindet sich eine weniger effiziente Implementierung, die 3 Speicherplätze verbraucht; rechts befindet sich eine effizientere Implementierung.
Durch diese Anpassung können Entwickler 20.000 Gas-Einheiten sparen (da das Speichern eines ungenutzten Speicherslots 20.000 Gas kostet), aber jetzt werden nur noch zwei Speicherslots benötigt.
Da jeder Speicherslot Gas verbraucht, optimiert die Variablenpackung den Gasverbrauch, indem die Anzahl der benötigten Speicherslots reduziert wird.
Eine Variable kann mit unterschiedlichen Datentypen dargestellt werden, aber die Betriebskosten variieren je nach Typ. Die Auswahl des geeigneten Datentyps hilft, die Gasnutzung zu optimieren.
Zum Beispiel können in Solidity Ganzzahlen in verschiedene Größen unterteilt werden: uint8, uint16, uint32, usw. Da die EVM in 256-Bit-Einheiten arbeitet, bedeutet die Verwendung von uint8, dass die EVM sie zuerst in uint256 konvertieren muss, und diese Konvertierung verursacht zusätzliche Gas-Kosten.
Wir können die Gas-Kosten von uint8 und uint256 mithilfe des Codes im Diagramm vergleichen. Die UseUint()-Funktion verbraucht 120.382 Gas-Einheiten, während die UseUInt8()-Funktion 166.111 Gas-Einheiten verbraucht.
Für sich genommen ist die Verwendung von uint256 günstiger als uint8. Wenn wir jedoch die zuvor vorgeschlagene Variable-Packing-Optimierung anwenden, macht es einen Unterschied. Wenn Entwickler vier uint8-Variablen in einen einzelnen Speicherplatz packen können, ist der Gesamtaufwand für die Iteration über sie niedriger als bei der Verwendung von vier uint256-Variablen. In diesem Fall kann der Smart Contract den Speicherplatz einmal lesen und schreiben und alle vier uint8-Variablen in einem einzigen Vorgang in den Speicher laden.
Wenn die Daten auf 32 Bytes begrenzt werden können, wird empfohlen, anstelle von Bytes oder Strings den Datentyp bytes32 zu verwenden. Im Allgemeinen verbrauchen Variablen mit fester Größe weniger Gas als Variablen mit dynamischer Größe. Wenn die Byte-Länge begrenzt werden kann, versuchen Sie, die kleinste Länge von bytes1 bis bytes32 zu wählen.
In Solidity können Datensammlungen mit zwei Datentypen dargestellt werden: Arrays und Mappings, von denen jeder eine eigene Syntax und Struktur aufweist.
Mappings sind in den meisten Fällen allgemein effizienter und kostengünstiger, während Arrays iterierbar sind und die Datenverpackung unterstützen. Daher wird empfohlen, beim Verwalten von Datenlisten die Verwendung von Mappings priorisiert, es sei denn, eine Iteration ist erforderlich oder der Gasverbrauch durch Datenverpackung optimiert werden kann.
Variablen, die in den Funktionsparametern deklariert sind, können entweder im calldata oder im Speicher gespeichert werden. Der Hauptunterschied besteht darin, dass der Speicher von der Funktion verändert werden kann, während calldata unveränderlich ist.
Behalten Sie dieses Prinzip im Hinterkopf: Wenn Funktionparameter schreibgeschützt sind, verwenden Sie bevorzugt calldata anstelle von memory. Dadurch werden unnötige Kopiervorgänge von Funktion calldata in memory vermieden.
Beispiel 1: Mit Speicher verwenden
Bei Verwendung des Speicherschlüsselworts werden die Werte des Arrays beim ABI-Decodieren aus dem codierten Calldaten in den Speicher kopiert. Die Ausführungskosten dieses Codeblocks betragen 3.694 Gas-Einheiten.
Beispiel 2: Verwendung von calldata
Beim direkten Lesen von Werten aus Calldata wird der Zwischenspeichervorgang übersprungen. Diese Optimierung reduziert die Ausführungskosten auf nur 2.413 Gas-Einheiten, was zu einer Verbesserung der Gas-Effizienz um 35 % führt.
Konstante/Unveränderliche Variablen werden nicht im Speicher des Vertrags gespeichert. Diese Variablen werden zur Kompilierzeit berechnet und im Bytcode des Vertrags gespeichert. Daher ist ihr Zugriffskosten viel niedriger im Vergleich zu Speichervariablen. Es wird empfohlen, die Schlüsselwörter Konstant oder Unveränderlich wann immer möglich zu verwenden.
Wenn Entwickler sicher sein können, dass arithmetische Operationen nicht zu einem Überlauf oder Unterlauf führen, können sie das unchecked-Schlüsselwort, das in Solidity v0.8.0 eingeführt wurde, verwenden, um unnötige Überlauf- oder Unterlaufprüfungen zu vermeiden und so Gas-Kosten zu sparen.
In der folgenden Abbildung ist die bedingt eingeschränkte i
Darüber hinaus erfordern Compiler-Versionen 0.8.0 und höher nicht mehr die Verwendung der SafeMath-Bibliothek, da der Compiler selbst jetzt einen integrierten Schutz gegen Überlauf und Unterlauf enthält.
Der Code der Modifier ist in die Funktionen eingebettet, die sie modifizieren. Jedes Mal, wenn ein Modifier verwendet wird, wird sein Code dupliziert, was die Bytecode-Größe erhöht und den Gasverbrauch steigert. Hier ist eine Möglichkeit, die Gaskosten für Modifier zu optimieren:
Vor Optimierung:
Nach der Optimierung:
In diesem Beispiel wird durch die Umstrukturierung der Logik in eine interne Funktion _checkOwner(), die im Modifier wiederverwendet werden kann, die Bytecode-Größe reduziert und die Gas-Kosten gesenkt.
Für || (ODER) und && (UND) Operatoren werden logische Operationen mit Kurzschlussauswertung durchgeführt, was bedeutet, dass wenn die erste Bedingung ausreicht, um das Ergebnis des logischen Ausdrucks zu bestimmen, die zweite Bedingung nicht ausgewertet wird.
Um den Gasverbrauch zu optimieren, sollten Bedingungen mit geringeren Berechnungskosten zuerst platziert werden, damit potenziell teure Berechnungen übersprungen werden können.
Wenn es ungenutzte Funktionen oder Variablen im Vertrag gibt, wird empfohlen, sie zu löschen. Dies ist der direkteste Weg, um die Bereitstellungskosten des Vertrags zu reduzieren und die Vertragsgröße klein zu halten.
Hier sind einige praktische Vorschläge:
Verwenden Sie die effizientesten Algorithmen für Berechnungen. Wenn der Vertrag direkt bestimmte Berechnungsergebnisse verwendet, sollten redundante Berechnungen entfernt werden. Grundsätzlich sollten alle ungenutzten Berechnungen gelöscht werden. In Ethereum können Entwickler Gasbelohnungen erhalten, indem sie Speicherplatz freigeben. Wenn eine Variable nicht mehr benötigt wird, sollte sie mit dem Lösch-Schlüsselwort gelöscht oder auf ihren Standardwert gesetzt werden.
Schleifenoptimierung: Vermeiden Sie teure Schleifenoperationen, versuchen Sie, Schleifen zu fusionieren und wiederholte Berechnungen aus dem Schleifenkörper zu verschieben.
Vorkompilierte Verträge bieten komplexe Bibliotheksfunktionen wie Kryptografie- und Hash-Operationen. Da der Code nicht auf dem EVM ausgeführt wird, sondern lokal auf dem Client-Node läuft, wird weniger Gas benötigt. Die Verwendung von vorkompilierten Verträgen kann Gas sparen, indem die Rechenlast reduziert wird, die zur Ausführung des Smart Contracts erforderlich ist.
Beispiele für vorcompilierte Verträge sind der Elliptic Curve Digital Signature Algorithm (ECDSA) und der SHA2-256-Hashing-Algorithmus. Durch die Verwendung dieser vorcompilierten Verträge in Smart Contracts können Entwickler die Gas-Kosten senken und die Effizienz der Anwendung verbessern.
Für eine vollständige Liste der von dem Ethereum-Netzwerk unterstützten vorkompilierten Verträge, siehe diesen Link [4].
Inline-Assembly ermöglicht Entwicklern das Schreiben von Low-Level-, aber effizientem Code, der vom EVM direkt ausgeführt werden kann, ohne teure Solidity-Operationen zu verwenden. Inline-Assembly bietet auch eine präzisere Kontrolle über den Speicher- und Speichernutzung, was die Gas-Kosten weiter reduziert. Darüber hinaus kann Inline-Assembly einige komplexe Operationen durchführen, die mit Solidity allein schwer umzusetzen sind, was mehr Flexibilität für die Optimierung des Gasverbrauchs bietet.
Hier ist ein Beispiel für die Verwendung von Inline-Assembly, um Gas zu sparen:
Wie im obigen Beispiel zu sehen ist, hat der zweite Fall, der Inline-Assembly verwendet, eine höhere Gas-Effizienz im Vergleich zum Standardfall.
Die Verwendung von Inline-Assembly kann jedoch auch Risiken mit sich bringen und ist fehleranfällig. Daher sollte sie vorsichtig verwendet werden und wird nur erfahrenen Entwicklern empfohlen.
Layer 2 Lösungen können die Menge an Daten reduzieren, die auf der Ethereum-Hauptkette gespeichert und berechnet werden müssen.
Layer-2-Lösungen wie Rollups, Sidechains und State Channels entlasten die Transaktionsverarbeitung von der Haupt-Ethereum-Kette und ermöglichen schnellere und günstigere Transaktionen.
Durch das Bündeln einer großen Anzahl von Transaktionen reduzieren diese Lösungen die Anzahl der On-Chain-Transaktionen, was wiederum die Gasgebühren senkt. Die Verwendung von Layer-2-Lösungen verbessert auch die Skalierbarkeit von Ethereum, wodurch mehr Benutzer und Anwendungen am Netzwerk teilnehmen können, ohne dass es zu Überlastungen kommt.
Es gibt mehrere Optimierungstools zur Verfügung, wie der solc Optimierer, Truffles Build-Optimizer und Remixs Solidity-Compiler.
Diese Tools können die Bytecode-Größe minimieren, nicht verwendeten Code entfernen und die Anzahl der für die Ausführung von Smart Contracts erforderlichen Operationen reduzieren. In Kombination mit anderen Gas-Optimierungsbibliotheken wie „solmate“ können Entwickler die Gas-Kosten effektiv senken und die Effizienz von Smart Contracts verbessern.
Die Optimierung des Gasverbrauchs ist ein wichtiger Schritt für Entwickler, da dies nicht nur die Transaktionskosten minimiert, sondern auch die Effizienz von Smart Contracts in EVM-kompatiblen Netzwerken verbessert. Durch Priorisierung kostensparender Operationen, Reduzierung des Speicherplatzverbrauchs, Nutzung von Inline-Assembly und Befolgung anderer bewährter Verfahren, die in diesem Artikel erörtert werden, können Entwickler den Gasverbrauch von Verträgen effektiv senken.
Es ist jedoch wichtig zu beachten, dass Entwickler während des Optimierungsprozesses Vorsicht walten lassen müssen, um Sicherheitsanfälligkeiten zu vermeiden. Bei der Optimierung des Codes und der Reduzierung des Gasverbrauchs darf die inhärente Sicherheit des Smart Contracts niemals beeinträchtigt werden.
[1] https://ethereum.org/de/developers/docs/gas/
[2] https://ethereum.github.io/yellowpaper/paper.pdf
[3]https://www.evm.codes/
[4] https://www.evm.codes/precompiled
Durch die Befolgung dieser Praktiken können Entwickler den Gasverbrauch in Smart Contracts reduzieren, Transaktionskosten senken und effizientere und benutzerfreundlichere Anwendungen erstellen.
Gasgebühren auf dem Ethereum-Mainnet waren schon immer ein großes Problem, insbesondere während Zeiten von Netzwerküberlastung. Zu Spitzenzeiten müssen Benutzer oft extrem hohe Transaktionsgebühren zahlen. Daher ist die Optimierung der Gas-Kosten während der Entwicklung von Smart Contracts entscheidend. Die Gasoptimierung kann nicht nur die Transaktionskosten effektiv reduzieren, sondern auch die Transaktionseffizienz verbessern und den Benutzern ein wirtschaftlicheres und effizienteres Blockchain-Erlebnis bieten.
In diesem Artikel wird der Gasgebührenmechanismus der Ethereum Virtual Machine (EVM), die Kernkonzepte im Zusammenhang mit der Optimierung von Gasgebühren und bewährte Verfahren zur Optimierung von Gasgebühren bei der Entwicklung von Smart Contracts erläutert. Es wird gehofft, dass dieser Inhalt Entwickler inspiriert und unterstützt, während er auch gewöhnlichen Benutzern hilft, das Funktionieren des EVM-Gasgebührensystems besser zu verstehen und so die Herausforderungen im Blockchain-Ökosystem anzugehen.
In EVM-kompatiblen Netzwerken bezieht sich "Gas" auf die Einheit, die zur Messung der Rechenleistung erforderlich ist, um bestimmte Operationen auszuführen.
Die folgende Abbildung veranschaulicht die Struktur der EVM. In der Abbildung wird der Gasverbrauch in drei Teile aufgeteilt: Ausführung von Operationen, externe Nachrichtenaufrufe sowie Lese- und Schreibvorgänge des Arbeitsspeichers und des Speichers.
Quelle: Offizielle Ethereum-Website[1]
Seit der Aktivierung von EIP-1559 (London Hard Fork) werden die Gasgebühren mit folgender Formel berechnet:
Gasgebühr = verwendete Gaseinheiten * (Grundgebühr + Prioritätsgebühr)
Die Grundgebühr wird verbrannt, während die Prioritätsgebühr als Anreiz dient, die Validatoren dazu zu ermutigen, die Transaktion in die Blockchain aufzunehmen. Wenn Sie eine Transaktion mit einer höheren Prioritätsgebühr senden, erhöht sich die Wahrscheinlichkeit, dass die Transaktion im nächsten Block enthalten ist. Dies ähnelt einem „Trinkgeld“, das von Benutzern an die Validatoren gezahlt wird.
Beim Kompilieren eines Smart Contracts mit Solidity wird der Vertrag in eine Reihe von "Betriebscodes" oder Opcodes umgewandelt.
Jeder Opcode (wie das Erstellen eines Vertrags, das Durchführen von Nachrichtenaufrufen, der Zugriff auf den Kontospeicher und das Ausführen von Operationen auf der virtuellen Maschine) hat eine zugehörige Gasverbrauchskosten, die im Ethereum Yellow Paper[2] dokumentiert sind.
Nach mehreren EIP-Änderungen wurden die Gasgebühren einiger Opcodes angepasst, die von den Werten im Yellow Paper abweichen können. Für detaillierte Informationen zu den neuesten Kosten von Opcodes, siehe diese Quelle[3].
Das Kernkonzept der Gasoptimierung besteht darin, kosteneffiziente Operationen auf der EVM-Blockchain zu priorisieren und Operationen zu vermeiden, die hohe Gas kosten verursachen.
Im EVM sind die folgenden Operationen vergleichsweise kostengünstig:
Zu den kostspieligen Operationen gehören:
Basierend auf den oben genannten Grundkonzepten haben wir eine Liste bewährter Verfahren zur Optimierung der Gasgebühr für die Entwicklergemeinschaft zusammengestellt. Durch die Befolgung dieser Praktiken können Entwickler den Gasverbrauch von Smart Contracts reduzieren, Transaktionskosten senken und effizientere und benutzerfreundlichere Anwendungen erstellen.
In Solidity, Speicher ist eine begrenzte Ressource, und sein Gasverbrauch ist deutlich höher als der von Memory. Jedes Mal, wenn ein Smart Contract aus dem Speicher liest oder in ihn schreibt, entsteht ein hoher Gasverbrauch.
Gemäß der Definition im Ethereum Yellow Paper ist der Speicheraufwand für Speicheroperationen mehr als 100-mal höher als der Speicheraufwand für Arbeitsspeicheroperationen. Zum Beispiel kosten Opcodes wie sload und sstore im besten Fall mindestens 100 Gas-Einheiten, während Arbeitsspeicheroperationen wie mload und mstore nur 3 Gas-Einheiten verbrauchen.
Methoden zur Begrenzung des Speicherverbrauchs umfassen:
Die Anzahl der Speicherplätze, die in einem Smart Contract verwendet werden, und die Art und Weise, wie Entwickler Daten darstellen, können den Gasverbrauch erheblich beeinflussen.
Der Solidity-Compiler packt während des Kompilierungsprozesses aufeinanderfolgende Speichervariablen unter Verwendung von 32-Byte-Speicherslots als Grundlage für die Variablenspeicherung. Die Variablenverpackung bezieht sich auf die Praxis, Variablen so anzuordnen, dass mehrere Variablen in einen einzigen Speicherslot passen.
Links befindet sich eine weniger effiziente Implementierung, die 3 Speicherplätze verbraucht; rechts befindet sich eine effizientere Implementierung.
Durch diese Anpassung können Entwickler 20.000 Gas-Einheiten sparen (da das Speichern eines ungenutzten Speicherslots 20.000 Gas kostet), aber jetzt werden nur noch zwei Speicherslots benötigt.
Da jeder Speicherslot Gas verbraucht, optimiert die Variablenpackung den Gasverbrauch, indem die Anzahl der benötigten Speicherslots reduziert wird.
Eine Variable kann mit unterschiedlichen Datentypen dargestellt werden, aber die Betriebskosten variieren je nach Typ. Die Auswahl des geeigneten Datentyps hilft, die Gasnutzung zu optimieren.
Zum Beispiel können in Solidity Ganzzahlen in verschiedene Größen unterteilt werden: uint8, uint16, uint32, usw. Da die EVM in 256-Bit-Einheiten arbeitet, bedeutet die Verwendung von uint8, dass die EVM sie zuerst in uint256 konvertieren muss, und diese Konvertierung verursacht zusätzliche Gas-Kosten.
Wir können die Gas-Kosten von uint8 und uint256 mithilfe des Codes im Diagramm vergleichen. Die UseUint()-Funktion verbraucht 120.382 Gas-Einheiten, während die UseUInt8()-Funktion 166.111 Gas-Einheiten verbraucht.
Für sich genommen ist die Verwendung von uint256 günstiger als uint8. Wenn wir jedoch die zuvor vorgeschlagene Variable-Packing-Optimierung anwenden, macht es einen Unterschied. Wenn Entwickler vier uint8-Variablen in einen einzelnen Speicherplatz packen können, ist der Gesamtaufwand für die Iteration über sie niedriger als bei der Verwendung von vier uint256-Variablen. In diesem Fall kann der Smart Contract den Speicherplatz einmal lesen und schreiben und alle vier uint8-Variablen in einem einzigen Vorgang in den Speicher laden.
Wenn die Daten auf 32 Bytes begrenzt werden können, wird empfohlen, anstelle von Bytes oder Strings den Datentyp bytes32 zu verwenden. Im Allgemeinen verbrauchen Variablen mit fester Größe weniger Gas als Variablen mit dynamischer Größe. Wenn die Byte-Länge begrenzt werden kann, versuchen Sie, die kleinste Länge von bytes1 bis bytes32 zu wählen.
In Solidity können Datensammlungen mit zwei Datentypen dargestellt werden: Arrays und Mappings, von denen jeder eine eigene Syntax und Struktur aufweist.
Mappings sind in den meisten Fällen allgemein effizienter und kostengünstiger, während Arrays iterierbar sind und die Datenverpackung unterstützen. Daher wird empfohlen, beim Verwalten von Datenlisten die Verwendung von Mappings priorisiert, es sei denn, eine Iteration ist erforderlich oder der Gasverbrauch durch Datenverpackung optimiert werden kann.
Variablen, die in den Funktionsparametern deklariert sind, können entweder im calldata oder im Speicher gespeichert werden. Der Hauptunterschied besteht darin, dass der Speicher von der Funktion verändert werden kann, während calldata unveränderlich ist.
Behalten Sie dieses Prinzip im Hinterkopf: Wenn Funktionparameter schreibgeschützt sind, verwenden Sie bevorzugt calldata anstelle von memory. Dadurch werden unnötige Kopiervorgänge von Funktion calldata in memory vermieden.
Beispiel 1: Mit Speicher verwenden
Bei Verwendung des Speicherschlüsselworts werden die Werte des Arrays beim ABI-Decodieren aus dem codierten Calldaten in den Speicher kopiert. Die Ausführungskosten dieses Codeblocks betragen 3.694 Gas-Einheiten.
Beispiel 2: Verwendung von calldata
Beim direkten Lesen von Werten aus Calldata wird der Zwischenspeichervorgang übersprungen. Diese Optimierung reduziert die Ausführungskosten auf nur 2.413 Gas-Einheiten, was zu einer Verbesserung der Gas-Effizienz um 35 % führt.
Konstante/Unveränderliche Variablen werden nicht im Speicher des Vertrags gespeichert. Diese Variablen werden zur Kompilierzeit berechnet und im Bytcode des Vertrags gespeichert. Daher ist ihr Zugriffskosten viel niedriger im Vergleich zu Speichervariablen. Es wird empfohlen, die Schlüsselwörter Konstant oder Unveränderlich wann immer möglich zu verwenden.
Wenn Entwickler sicher sein können, dass arithmetische Operationen nicht zu einem Überlauf oder Unterlauf führen, können sie das unchecked-Schlüsselwort, das in Solidity v0.8.0 eingeführt wurde, verwenden, um unnötige Überlauf- oder Unterlaufprüfungen zu vermeiden und so Gas-Kosten zu sparen.
In der folgenden Abbildung ist die bedingt eingeschränkte i
Darüber hinaus erfordern Compiler-Versionen 0.8.0 und höher nicht mehr die Verwendung der SafeMath-Bibliothek, da der Compiler selbst jetzt einen integrierten Schutz gegen Überlauf und Unterlauf enthält.
Der Code der Modifier ist in die Funktionen eingebettet, die sie modifizieren. Jedes Mal, wenn ein Modifier verwendet wird, wird sein Code dupliziert, was die Bytecode-Größe erhöht und den Gasverbrauch steigert. Hier ist eine Möglichkeit, die Gaskosten für Modifier zu optimieren:
Vor Optimierung:
Nach der Optimierung:
In diesem Beispiel wird durch die Umstrukturierung der Logik in eine interne Funktion _checkOwner(), die im Modifier wiederverwendet werden kann, die Bytecode-Größe reduziert und die Gas-Kosten gesenkt.
Für || (ODER) und && (UND) Operatoren werden logische Operationen mit Kurzschlussauswertung durchgeführt, was bedeutet, dass wenn die erste Bedingung ausreicht, um das Ergebnis des logischen Ausdrucks zu bestimmen, die zweite Bedingung nicht ausgewertet wird.
Um den Gasverbrauch zu optimieren, sollten Bedingungen mit geringeren Berechnungskosten zuerst platziert werden, damit potenziell teure Berechnungen übersprungen werden können.
Wenn es ungenutzte Funktionen oder Variablen im Vertrag gibt, wird empfohlen, sie zu löschen. Dies ist der direkteste Weg, um die Bereitstellungskosten des Vertrags zu reduzieren und die Vertragsgröße klein zu halten.
Hier sind einige praktische Vorschläge:
Verwenden Sie die effizientesten Algorithmen für Berechnungen. Wenn der Vertrag direkt bestimmte Berechnungsergebnisse verwendet, sollten redundante Berechnungen entfernt werden. Grundsätzlich sollten alle ungenutzten Berechnungen gelöscht werden. In Ethereum können Entwickler Gasbelohnungen erhalten, indem sie Speicherplatz freigeben. Wenn eine Variable nicht mehr benötigt wird, sollte sie mit dem Lösch-Schlüsselwort gelöscht oder auf ihren Standardwert gesetzt werden.
Schleifenoptimierung: Vermeiden Sie teure Schleifenoperationen, versuchen Sie, Schleifen zu fusionieren und wiederholte Berechnungen aus dem Schleifenkörper zu verschieben.
Vorkompilierte Verträge bieten komplexe Bibliotheksfunktionen wie Kryptografie- und Hash-Operationen. Da der Code nicht auf dem EVM ausgeführt wird, sondern lokal auf dem Client-Node läuft, wird weniger Gas benötigt. Die Verwendung von vorkompilierten Verträgen kann Gas sparen, indem die Rechenlast reduziert wird, die zur Ausführung des Smart Contracts erforderlich ist.
Beispiele für vorcompilierte Verträge sind der Elliptic Curve Digital Signature Algorithm (ECDSA) und der SHA2-256-Hashing-Algorithmus. Durch die Verwendung dieser vorcompilierten Verträge in Smart Contracts können Entwickler die Gas-Kosten senken und die Effizienz der Anwendung verbessern.
Für eine vollständige Liste der von dem Ethereum-Netzwerk unterstützten vorkompilierten Verträge, siehe diesen Link [4].
Inline-Assembly ermöglicht Entwicklern das Schreiben von Low-Level-, aber effizientem Code, der vom EVM direkt ausgeführt werden kann, ohne teure Solidity-Operationen zu verwenden. Inline-Assembly bietet auch eine präzisere Kontrolle über den Speicher- und Speichernutzung, was die Gas-Kosten weiter reduziert. Darüber hinaus kann Inline-Assembly einige komplexe Operationen durchführen, die mit Solidity allein schwer umzusetzen sind, was mehr Flexibilität für die Optimierung des Gasverbrauchs bietet.
Hier ist ein Beispiel für die Verwendung von Inline-Assembly, um Gas zu sparen:
Wie im obigen Beispiel zu sehen ist, hat der zweite Fall, der Inline-Assembly verwendet, eine höhere Gas-Effizienz im Vergleich zum Standardfall.
Die Verwendung von Inline-Assembly kann jedoch auch Risiken mit sich bringen und ist fehleranfällig. Daher sollte sie vorsichtig verwendet werden und wird nur erfahrenen Entwicklern empfohlen.
Layer 2 Lösungen können die Menge an Daten reduzieren, die auf der Ethereum-Hauptkette gespeichert und berechnet werden müssen.
Layer-2-Lösungen wie Rollups, Sidechains und State Channels entlasten die Transaktionsverarbeitung von der Haupt-Ethereum-Kette und ermöglichen schnellere und günstigere Transaktionen.
Durch das Bündeln einer großen Anzahl von Transaktionen reduzieren diese Lösungen die Anzahl der On-Chain-Transaktionen, was wiederum die Gasgebühren senkt. Die Verwendung von Layer-2-Lösungen verbessert auch die Skalierbarkeit von Ethereum, wodurch mehr Benutzer und Anwendungen am Netzwerk teilnehmen können, ohne dass es zu Überlastungen kommt.
Es gibt mehrere Optimierungstools zur Verfügung, wie der solc Optimierer, Truffles Build-Optimizer und Remixs Solidity-Compiler.
Diese Tools können die Bytecode-Größe minimieren, nicht verwendeten Code entfernen und die Anzahl der für die Ausführung von Smart Contracts erforderlichen Operationen reduzieren. In Kombination mit anderen Gas-Optimierungsbibliotheken wie „solmate“ können Entwickler die Gas-Kosten effektiv senken und die Effizienz von Smart Contracts verbessern.
Die Optimierung des Gasverbrauchs ist ein wichtiger Schritt für Entwickler, da dies nicht nur die Transaktionskosten minimiert, sondern auch die Effizienz von Smart Contracts in EVM-kompatiblen Netzwerken verbessert. Durch Priorisierung kostensparender Operationen, Reduzierung des Speicherplatzverbrauchs, Nutzung von Inline-Assembly und Befolgung anderer bewährter Verfahren, die in diesem Artikel erörtert werden, können Entwickler den Gasverbrauch von Verträgen effektiv senken.
Es ist jedoch wichtig zu beachten, dass Entwickler während des Optimierungsprozesses Vorsicht walten lassen müssen, um Sicherheitsanfälligkeiten zu vermeiden. Bei der Optimierung des Codes und der Reduzierung des Gasverbrauchs darf die inhärente Sicherheit des Smart Contracts niemals beeinträchtigt werden.
[1] https://ethereum.org/de/developers/docs/gas/
[2] https://ethereum.github.io/yellowpaper/paper.pdf
[3]https://www.evm.codes/
[4] https://www.evm.codes/precompiled