22 Minuten Lesezeit
Übersicht
Eine feste Regel bei der Entwicklung auf EVM-Blockchains lautet, dass einmal bereitgestellte Smart Contracts nicht mehr geändert werden können. Smart Contracts werden oft als „unveränderlich“ bezeichnet, was gewährleistet, dass der Code, mit dem Entwickler arbeiten, manipulationssicher und transparent ist. Diese Philosophie kommt denjenigen zugute, die mit Smart Contracts interagieren, jedoch nicht immer denen, die sie schreiben. Entwickler, die Smart Contracts erstellen, müssen stets sicherstellen, dass diese allumfassend und fehlerfrei sind und jeden Randfall abdecken. Dies ist oft der Fall, aber nicht immer, und genau hier entsteht der Bedarf an aktualisierbaren Smart Contracts.
Dank des Ansatzes mit aktualisierbaren Smart Contracts hat ein Entwickler bei einem Fehler, einer fehlerhaften Logik oder einer fehlenden Funktion in Ihrem Smart Contract die Möglichkeit, diesen Smart Contract zu aktualisieren und stattdessen einen neuen zu implementieren.
In diesem Tutorial werden wir genau zeigen, wie das funktioniert, indem wir mithilfe von OpenZeppelin und Hardhat einen aktualisierbaren Smart Contract von Grund auf erstellen und bereitstellen.
Was wir tun werden
- Erstellen Sie einen aktualisierbaren Smart Contract mithilfe des „Hardhat Upgrades “-Plugins von OpenZeppelin
- Kompilieren und stellen Sie den Vertrag auf Polygon PoS (Amoy-Testnetz) mit Hardhat 3 bereit
- Überprüfen Sie den Proxy und die Implementierung auf Polygonscan (Amoy)
- Den Vertrag aktualisieren und die Ergebnisse überprüfen
Was Sie benötigen
- Node.js v22+ und ein Paketmanager (npm, yarn oder pnpm)
- MetaMask mit Polygon PoS (Amoy) hinzugefügt (siehe Polygons MetaMask-Anleitung)
- Amoy-Testnet-POL aus einem Faucet (z. B. Quicknode Polygon Amoy Faucet)
- Ein Quicknode-Endpunkt für Polygon PoS → Amoy (auf der Quicknode-Seite zu Polygon PoS erstellen)
- Erfahrung mit Solidity
- Kenntnisse über aktualisierbare Proxys (siehe „Eine Einführung in aktualisierbare Smart Contracts“)
Einrichten der Entwicklungsumgebung
Wir benötigen lokal einen neuen Ordner, in dem unser Projekt für dieses Tutorial gespeichert wird. Wir nennen unseren Ordner „UpgradeableContracts“, aber Sie können ihn beliebig benennen. Führen Sie diese Befehle in Ihrem Terminal aus, um den Ordner zu erstellen und in ihn zu wechseln:
mkdir UpgradeableContracts
cd UpgradeableContracts
Super! Jetzt, da wir eine leere Leinwand haben, auf der wir arbeiten können, können wir mit dem Malen loslegen. Geben Sie den folgenden Befehl ein, um Ihr „Hardhat“-Projekt zu erstellen:
npx hardhat --init
Es werden Ihnen einige Fragen gestellt:
- Wenn Sie gefragt werden, welche Hardhat-Version Sie verwenden möchten, wählen Sie die Option „Hardhat 3“ aus, die standardmäßig ausgewählt ist.
- Wenn Sie dazu aufgefordert werden
Welche Art von Projekt möchten Sie starten?, wählen Sie dieMokka-EtherOption. - Sie werden aufgefordert, Abhängigkeiten zu installieren. Geben Sie Folgendes ein:
Yund drücken Sie die Eingabetaste. - Wenn Sie den Anweisungen auf dem Bildschirm folgen, sollte Ihr Arbeitsbereich in etwa so aussehen:
Einrichtung der Hardhat-Konfiguration
Herzlichen Glückwunsch! Sie haben Hardhat soeben erfolgreich installiert und initialisiert!
Sobald die Installation abgeschlossen ist, sollten Sie nun über alles verfügen, was Sie zur Entwicklung, zum Testen und zur Bereitstellung von Smart Contracts auf der Blockchain benötigen. Da wir mit aktualisierbaren Smart Contracts arbeiten werden, müssen wir noch einige weitere Abhängigkeiten installieren. Führen Sie die folgenden Befehle in Ihrem Terminal aus:
npm i -D @nomicfoundation/hardhat-ethers @nomicfoundation/hardhat-verify
npm i @openzeppelin/contracts dotenv
@openzeppelin/contracts ist das Paket, mit dem wir unsere Smart Contracts so bereitstellen können, dass sie aktualisierbar sind. @nomicfoundation/hardhat-verify ist ein Hardhat-Plugin, mit dem wir unsere Verträge in der Blockchain überprüfen können. Dadurch kann jeder mit Ihren bereitgestellten Verträgen interagieren, was für Transparenz sorgt. Die Verwendung des Hardhat-Plugins ist die bequemste Methode, um unsere Verträge zu verifizieren. @nomicfoundation/hardhat-ethers ist ein Plugin, das die ethers.js-Bibliothek in Hardhat integriert und es uns ermöglicht, mit der Blockchain zu interagieren.
Für die Verwaltung der Umgebungsvariablen wird das Paket „dotenv“ verwendet. Erstellen Sie im Stammverzeichnis Ihres Projekts eine Datei namens „.env “. In dieser Datei werden sensible Daten wie Ihr privater Schlüssel und Ihre API-Schlüssel gespeichert. Fügen Sie „.env“ unbedingt zu Ihrer „.gitignore“-Datei hinzu, damit Sie die Datei nicht versehentlich in ein öffentliches Repository pushen, falls Sie Git verwenden. So sollte Ihre „.env“-Datei aussehen:
RPC_URL="DEINE_QUICKNODE_RPC_URL"
PRIVATE_KEY="IHR_METAMASK-PRIVATSCHLÜSSEL"
ETHERSCAN_API_KEY="IHR_ETHERSCAN_API_SCHLÜSSEL"
Ein großes Lob, wenn Sie dem Tutorial bis hierher folgen konnten. Sie haben soeben eine Entwicklungsumgebung für Smart Contracts mit Hardhat eingerichtet und zusätzliche Abhängigkeiten installiert, die es uns ermöglichen, aktualisierbare Smart Contracts bereitzustellen und zu überprüfen.
Zugriff auf einen Polygon-Amoy-Testnet-Knoten
Wir müssen unsere Smart Contracts im Polygon-Amoy-Testnetz bereitstellen. Dazu können wir uns hier ganz einfach für eine kostenlose Quicknode-Testversion anmelden und einen Endpunkt für das Polygon-Amoy-Testnetz erstellen.
Seite „Quicknode-Endpunkte“
Kopieren Sie die HTTPS-URL und fügen Sie sie in die Variable „RPC_URL“ in Ihrer .env-Datei ein. Falls Sie noch keine .env-Datei haben, erstellen Sie eine im Stammverzeichnis Ihres Projekts. In dieser Datei werden sensible Daten wie Ihr privater Schlüssel und Ihre API-Schlüssel gespeichert.
Gehen Sie anschließend zu Ihrem Profil auf Etherscan und wechseln Sie zum Reiter „API KEYS“. Falls Sie noch kein Konto haben, erstellen Sie hier eines. Hier erstellen Sie einen API-Schlüssel, mit dem Sie Ihre Smart Contracts in der Blockchain überprüfen können. Kopieren Sie den API-Schlüssel und fügen Sie ihn in die Variable ` ETHERSCAN_API_KEY ` in Ihrer `.env` -Datei ein.
Hinweis: Etherscan hat seine API auf Version 2 aktualisiert. Wenn Sie auf Etherscan einen API-Schlüssel erstellen, kann dieser auch für mehrere von Etherscan unterstützte Blockchains verwendet werden, darunter auch Polygon. Sie müssen also keinen separaten API-Schlüssel für PolygonScan erstellen.
Zuletzt öffnest du MetaMask und kopierst den privaten Schlüssel eines deiner Konten. Wie du auf deinen privaten Schlüssel zugreifen kannst, erfährst du in dieser kurzen Anleitung. Füge diesen privaten Schlüssel in die Variable PRIVATE_KEY in deiner .env-Datei ein.
Außerdem müssen Sie einige Amoy Testnet MATIC auf Ihrem Konto haben, um Ihre Smart Contracts zu deployen. Diese können Sie über diesen Faucet erhalten.
Öffnen Sie abschließend Ihre Datei „hardhat.config.ts“ und ersetzen Sie den gesamten Code durch Folgendes:
import type { HardhatUserConfig } from "hardhat/config";
import hardhatToolboxMochaEthersPlugin from "@nomicfoundation/hardhat-toolbox-mocha-ethers";
import hardhatVerify from "@nomicfoundation/hardhat-verify";
import 'dotenv/config';
const config: HardhatUserConfig = {
plugins: [
hardhatToolboxMochaEthersPlugin,
hardhatVerify,
],
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts",
},
solidity: {
profiles: {
default: {
version: "0.8.22",
},
},
},
verify: {
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
}
},
networks: {
amoy: {
type: "http",
url: process.env.RPC_URL!,
accounts: [process.env.PRIVATE_KEY!],
},
},
};
export default config;
In den ersten Zeilen haben wir einige Bibliotheken importiert, die wir benötigen werden.
Um sicherzustellen, dass alles ordnungsgemäß funktioniert, speichern Sie alle Ihre Dateien und kompilieren Sie die Verträge erneut, indem Sie folgenden Befehl ausführen:
npx hardhat compile
Wenn Sie alle Schritte korrekt befolgt haben, kompiliert Hardhat Ihre Smart Contracts und zeigt eine Bestätigungsmeldung an. Nun sind wir bereit, unsere Smart Contracts bereitzustellen.
Erstellung unserer Smart Contracts
In diesem Abschnitt werden wir vier Smart Contracts erstellen. Die ersten beiden Contracts sind unsere Implementierungs-Contracts, die wir V1.sol und V2.sol nennen werden. Der dritte Contract ist der Contract „TransparentUpgradeableProxy.sol“ – ein Proxy-Contract, der auf unsere Implementierungs-Contracts verweist. Der vierte Contract ist der Contract „ProxyAdmin.sol“, der Eigentümer des Proxys ist und zur Verwaltung des Proxy-Contracts dient.
Wechseln Sie in den Ordner „contracts“ und löschen Sie die dort bereits vorhandene Datei „Counter.sol “. Dabei handelt es sich um eine von Hardhat bereitgestellte Standardvorlage für Smart Contracts, die wir nicht benötigen. Erstellen Sie vier neue Dateien im Ordner „contracts“ und benennen Sie diese „V1.sol“, „V2.sol“, „TransparentUpgradeableProxy.sol“ und „ProxyAdmin.sol“. Fügen Sie den folgenden Code jeweils in die entsprechenden Dateien ein.
cd Verträge
Touch V1.sol V2.sol TransparentUpgradeableProxy.sol ProxyAdmin.sol
- V1.sol
- V2.sol
- TransparentUpgradeableProxy.sol
- ProxyAdmin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
contract V1 {
uint public number;
function initialValue(uint _num) external {
number=_num;
}
function increase() external {
number += 1;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
contract V2 {
uint public number;
function initialValue(uint _num) external {
number=_num;
}
function increase() external {
number += 1;
}
function decrease() external {
number -= 1;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.22;
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {IERC1967} from "@openzeppelin/contracts/interfaces/IERC1967.sol";
import {ProxyAdmin} from "./ProxyAdmin.sol";
/**
* @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
* does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
* mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
* include them in the ABI so this interface must be used to interact with it.
*/
interface ITransparentUpgradeableProxy is IERC1967 {
/// @dev See {UUPSUpgradeable-upgradeToAndCall}
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable;
}
/**
* @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
* 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
* the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
* the proxy admin cannot fallback to the target implementation.
*
* These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
* dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
* call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
* allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
* interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
*
* NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
* inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
* mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
* fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
* implementation.
*
* NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
* meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
*
* IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
* immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
* overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
* undesirable state where the admin slot is different from the actual admin. Relying on the value of the admin slot
* is generally fine if the implementation is trusted.
*
* WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
* compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
* function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
* could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
*/
contract TransparentUpgradeableProxy is ERC1967Proxy {
// An immutable address for the admin to avoid unnecessary SLOADs before each call
// at the expense of removing the ability to change the admin once it's set.
// This is acceptable if the admin is always a ProxyAdmin instance or similar contract
// with its own ability to transfer the permissions to another account.
address private immutable _admin;
/**
* @dev The proxy caller is the current admin, and can't fallback to the proxy target.
*/
error ProxyDeniedAdminAccess();
/**
* @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
* backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
* {ERC1967Proxy-constructor}.
*/
constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
_admin = address(new ProxyAdmin(initialOwner));
// Set the storage value and emit an event for ERC-1967 compatibility
ERC1967Utils.changeAdmin(_proxyAdmin());
}
/**
* @dev Returns the admin of this proxy.
*/
function _proxyAdmin() internal view virtual returns (address) {
return _admin;
}
/**
* @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
*/
function _fallback() internal virtual override {
if (msg.sender == _proxyAdmin()) {
if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
revert ProxyDeniedAdminAccess();
} else {
_dispatchUpgradeToAndCall();
}
} else {
super._fallback();
}
}
/**
* @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
function _dispatchUpgradeToAndCall() private {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
ERC1967Utils.upgradeToAndCall(newImplementation, data);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.22;
import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract ProxyAdmin is Ownable {
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address,address)`
* and `upgradeAndCall(address,address,bytes)` are present, and `upgrade` must be used if no function should be called,
* while `upgradeAndCall` will invoke the `receive` function if the third argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeAndCall(address,address,bytes)` is present, and the third argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev Sets the initial owner who can perform upgrades.
*/
constructor(address initialOwner) Ownable(initialOwner) {}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
* See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
* - If `data` is empty, `msg.value` must be zero.
*/
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}
Hinweis: Die Verträge „TransparentUpgradeableProxy.sol“ und „ProxyAdmin.sol“ stammen direkt aus dem GitHub-Repository von OpenZeppelin. Sie finden diese Verträge hier.
Der V1-Vertrag ist recht einfach. Er enthält eine Zustandsvariable vom Typ „unsigned integer“ und zwei Funktionen. Die Funktion `initialValue()` setzt lediglich den Anfangswert der Variablen, während die Funktion `increase()` deren Wert um 1 erhöht.
Derzeit verweist unser Proxy auf die V1-Implementierung. Wir werden ihn auf die V2-Implementierung aktualisieren. Im V2-Vertrag fügen wir lediglich eine Funktion „decrease()“ hinzu, die den Wert der Variablen um 1 verringert.
Speichern Sie die Dateien, an denen Sie gearbeitet haben, und kehren Sie zum Terminal zurück. Vergewissern Sie sich, dass Sie sich im Projektverzeichnis befinden (z. B. „UpgradeableContracts“), und führen Sie dann diesen Befehl in Ihrem Terminal aus:
npx hardhat compile
Wenn Sie alles richtig gemacht haben, sollte das Terminal Ihnen anzeigen, dass zwei Solidity-Dateien erfolgreich kompiliert wurden. Wir sind nun bereit, unsere Deployment-Tools zu konfigurieren. Im nächsten Abschnitt erfahren Sie, welche Best Practices beim Deployment Ihrer Smart Contracts zu beachten sind.
Zündmodule für Schutzhelme
Zur Vorbereitung der Bereitstellung unserer Smart Contracts werden wir Hardhat-Ignition-Module verwenden. Mit Modulen lassen sich die Smart-Contract-Instanzen, die wir bereitstellen möchten, einfach konfigurieren und gruppieren. Wir werden ein Modul mit dem Namen ProxyModule.
Die ProxyModule wird die Einsatzformen der V1 und V2 Implementierungsverträge, die Transparenter, erweiterbarer Proxy Vollmachtsvertrag und der ProxyAdmin Vertrag. Außerdem werden wir einen Anfangswert von 10 für die V1 Vertrag in diesem Modul.
In der Zündung/Module Ordner, löschen Sie die bereits vorhandenen Counter.ts Datei. Erstellen Sie eine Datei mit dem Namen proxyModule.ts und fügen Sie den folgenden Code ein:
- proxyModule.ts
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
const proxyModule = buildModule("ProxyModule", (m) => {
const proxyAdminOwner = m.getAccount(0);
const v1 = m.contract("V1");
const v2 = m.contract("V2");
const proxy = m.contract("TransparentUpgradeableProxy", [
v1,
proxyAdminOwner,
"0x",
]);
const proxyAdminAddress = m.readEventArgument(
proxy,
"AdminChanged",
"newAdmin"
);
const proxyAdmin = m.contractAt("ProxyAdmin", proxyAdminAddress);
return { implementation: v1, proxyAdmin, proxy };
});
const v1Module = buildModule("V1Module", (m) => {
const { implementation, proxy, proxyAdmin } = m.useModule(proxyModule);
const v1 = m.contractAt("V1", proxy);
m.call(v1, "initialValue", [10]);
return { implementation, v1, proxy, proxyAdmin };
});
export default v1Module;
Nachdem wir nun unsere Module erstellt haben, fahren wir mit der Bereitstellung unserer Verträge fort!
Bereitstellung der Verträge
Nun sind wir bereit, unseren aktualisierbaren Smart Contract bereitzustellen! Führen Sie den folgenden Befehl aus, um den Bereitstellungsprozess zu starten:
npx hardhat ignition deploy ignition/modules/ProxyModule.ts --network amoy --verify
Die --Netzwerk Das Flag gibt das Netzwerk an, in dem wir unsere Smart Contracts bereitstellen möchten. In diesem Fall erfolgt die Bereitstellung im Polygon-Amoy-Testnetz.
Die --verify Das Flag überprüft unsere Verträge nach ihrer Bereitstellung automatisch auf Polygonscan.
Hinweis: Falls Sie die Bereitstellung bereits durchgeführt haben oder etwas schiefgelaufen ist, fügen Sie den
--ZurücksetzenFlag zum obigen Befehl, um eine erneute Bereitstellung zu erzwingen.
Terminalausgabe bei der Bereitstellung unseres Moduls
Aufschlüsselung der Produktion
Wenn Sie die Schritte korrekt befolgt haben, sollte in Ihrem Terminal eine Anzeige erscheinen, die der obigen Abbildung ähnelt. Die Terminalausgabe zeigt, dass die V1, V2, Transparenter, erweiterbarer Proxy, und ProxyAdmin Die Verträge wurden erfolgreich mit ihren jeweiligen Adressen bereitgestellt.
| Vertragsname | Beschreibung |
|---|---|
| ProxyModule#V1 | Die erste Version unseres Implementierungsvertrags mit einem Anfangswert und einer Erhöhung. Zur Erinnerung: Wir haben den Anfangswert im Modul auf 10 festgelegt. |
| ProxyModule#V2 | Die zweite Version unseres Implementierungsvertrags mit einer zusätzlichen Funktion zum Verringern. |
| ProxyModule#TransparentUpgradeableProxy | Der Proxy-Vertrag, der auf unseren aktuellen Implementierungsvertrag verweist. Derzeit verweist er auf V1. |
| ProxyModule#ProxyAdmin | Der Vertrag, der das Proxy aktualisieren kann. Nur der Eigentümer von „ProxyAdmin“ kann die Implementierung von „TransparentUpgradeableProxy“ aktualisieren. |
WICHTIG: Achten Sie darauf, sich die
ProxyModule#TransparentUpgradeableProxy, ProxyModule#V2, ProxyModule#ProxyAdminAdressen unter derVerwendete AdressenAbschnitt im Terminal, da wir diese später noch benötigen werden.
Vielleicht fragen Sie sich, was genau hinter den Kulissen vor sich geht. Lassen Sie uns einen Moment innehalten und das herausfinden.
Ein Blick unter die Haube
Wir werden uns die von uns bereitgestellten Smart Contracts genauer ansehen. Gehen Sie zunächst zu MetaMask und kopieren Sie die öffentliche Adresse des Kontos, das Sie zum Bereitstellen der Smart Contracts verwendet haben. Öffnen Sie den Amoy-Testnet-Explorer und suchen Sie nach Ihrer Kontoadresse.
Sie werden feststellen, dass auf Ihrem Konto vier Transaktionen stattgefunden haben:
- Bei den ersten beiden Transaktionen handelt es sich um die Bereitstellung des
V1undV2Durchführungsverträge. - Die dritte Transaktion ist die Bereitstellung der
Transparenter, erweiterbarer ProxyundProxyAdminVerträge - Die vierte Transaktion dient dazu, den Anfangswert von 10 in unserem Proxy-Vertrag festzulegen.
Seite „Kontobewegungen“ bei PolygonScan
Um die einzelnen Verträge anzuzeigen, können Sie auf den Link „Vertragserstellung“ unter dem Feld „An“ auf der Registerkarte „Transaktionen“ klicken.
Werfen wir einen Blick auf die Transparenter, erweiterbarer Proxy Vertrag. Klicken Sie auf den An Link in der dritten Transaktion (oben hervorgehoben). Dadurch gelangen Sie zur Vertragsseite. Gehen Sie anschließend zur Vertrag Registerkarte mit einem Häkchen. Es sollte in etwa so aussehen:
Registerkarte „Vertragsproxy“ in PolygonScan
Auf dieser Seite sehen wir den Quellcode unseres Transparenter, erweiterbarer Proxy Vertrag, was bedeutet, dass er erfolgreich verifiziert wurde. Aber woher wissen wir, ob die Implementierung dieses Proxy-Vertrags tatsächlich unser V1 Vertrag? Klicken Sie auf den Weitere Optionen Dropdown-Menü direkt über dem Quellcode (wie oben gezeigt). Klicken Sie auf Ist das ein Proxy?
Polygonscan-Proxy-Überprüfung
Sie werden anschließend aufgefordert, den Vertrag als Bevollmächtigter zu bestätigen. Klicken Sie auf Überprüfen. Es erscheint ein Popup-Fenster, in dem die Implementierungsadresse angezeigt wird. Sie werden feststellen, dass es sich dabei um dieselbe Adresse handelt wie unsere V1 Vertrag! Klicken Sie hier Speichern dann kehren Sie zu unserer Transparenter, erweiterbarer Proxy Vertragsseite. Sie sollten nun zwei neue Registerkarten sehen! Als Bevollmächtigter lesen und Als Bevollmächtigter schreiben.

Klicken Sie auf den Zahl Funktion unter der Als Bevollmächtigter lesen Registerkarte. Dort sollte der Wert 10 angezeigt werden, den wir zuvor als Anfangswert festgelegt haben. Wenn Sie auf Als Bevollmächtigter schreiben, dann sehen Sie die V1 Funktionen des Vertrags. Dies bestätigt, dass unser Proxy-Vertrag tatsächlich auf unseren V1 Umsetzungsvertrag.
Aufschlüsselung des Proxy-Vertrags
Die ProxyModule beschrieb mehrere Smart Contracts, die bereitgestellt werden sollen, nämlich die Transparenter, erweiterbarer Proxy und ProxyAdmin Verträge. Transparenter, erweiterbarer Proxy ist hier der Hauptvertrag. Dieser Vertrag enthält alle Änderungen an den Zustandsvariablen für unseren Implementierungsvertrag. Das bedeutet, dass der Implementierungsvertrag keinen eigenen Zustand verwaltet und sich bei der Speicherung tatsächlich auf den Proxy-Vertrag stützt.
Suchen Sie zur Veranschaulichung nach dem V1 die Vertragsadresse, die Sie zuvor im Amoy Block Explorer notiert haben. Wenn Sie zur Vertrag Registerkarte und klicken Sie auf Vertrag lesen, werden Sie feststellen, dass der Wert der Zustandsvariablen Zahl ist 0! Erinnern wir uns daran, dass wir den Wert von Zahl als 10, wenn wir es aus dem Proxy-Vertrag auslesen. Dies zeigt, dass der Implementierungsvertrag keinen eigenen Zustand verwaltet, sondern ausschließlich als Logikvertrag dient.
In diesem Szenario lautet der Proxy-Vertrag (Transparenter, erweiterbarer Proxy) ist der Wrapper für unseren Implementierungsvertrag (V1), und falls und sobald wir unseren Smart Contract aktualisieren müssen (über ProxyAdmin), stellen wir einfach einen weiteren Vertrag bereit und lassen unseren Proxy-Vertrag auf diesen Vertrag verweisen, wodurch dessen Zustand und zukünftige Funktionalität verbessert werden. Wie cool ist das denn!
Letztendlich haben wir den Code in keinem unserer Smart Contracts tatsächlich geändert, doch aus Sicht der Nutzer wurde der Hauptvertrag aktualisiert. Dieses Flussdiagramm verdeutlicht dies:
Flussdiagramm für aktualisierbare Smart Contracts
Aktualisierung des Vertrags von Version 1 auf Version 2
Nachdem wir nun ein solides Verständnis davon haben, was im Backend vor sich geht, kehren wir zu unserem Code zurück und aktualisieren unseren Vertrag! Erstellen Sie im Ordner „scripts“ eine neue Datei mit dem Namen „upgrade.ts“. Fügen Sie darin den folgenden Code ein:
- upgrade.ts
import hre from "hardhat";
async function main() {
const { ethers } = await hre.network.connect({
network: "amoy",
});
const [currentAccount] = await ethers.getSigners();
console.log("Current account:", currentAccount.address);
const proxy = "<INSERT_YOUR_PROXY_ADDRESS_HERE>";
const proxyAdmin = "<INSERT_YOUR_PROXY_ADMIN_ADDRESS_HERE>";
const v2Implementation = "<INSERT_YOUR_V2_IMPLEMENTATION_ADDRESS_HERE>";
const proxyAdminContract = await ethers.getContractAt("ProxyAdmin", proxyAdmin);
const v2 = await ethers.getContractAt("V2", v2Implementation);
const encodedFunctionCall = v2.interface.encodeFunctionData("decrease");
console.log("Upgrading to V2...");
const upgradeTx = await proxyAdminContract.connect(currentAccount).upgradeAndCall(proxy, v2Implementation, encodedFunctionCall);
await upgradeTx.wait();
console.log("Upgraded to V2");
const v2Proxy = await ethers.getContractAt("V2", proxy);
const currentValue = await v2Proxy.number();
console.log("Current value after upgrade and call:", currentValue.toString());
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
WICHTIG: Bevor wir unseren Vertrag verlängern, denken Sie bitte daran, Ihre
Transparenter, erweiterbarer Proxy,ProxyAdmin, undV2Vertragsadressen in die entsprechenden Variablen im obigen Code ein.
Führen wir nun dieses Skript im Terminal aus:
npx hardhat run scripts/upgrade.ts --network amoy
Aufschlüsselung des Upgrades
Im Grunde genommen ist hier Folgendes passiert: Wir haben die upgradeAndCall Funktion innerhalb der ProxyAdmin Vertrag: Zunächst den Implementierungsvertrag aktualisieren und anschließend den Rückgang Funktion. Wenn Sie die Terminalausgabe überprüfen, werden Sie feststellen, dass der aktuelle Wert nun 9 beträgt, was bedeutet, dass die Rückgang Die Funktion wurde nach dem Upgrade erfolgreich aufgerufen. Um sich noch einmal zu vergewissern, dass der Aufruf der „decrease“-Funktion erfolgreich war, kehren Sie zur Als Bevollmächtigter lesen Registerkarte des Transparenter, erweiterbarer Proxy Wenn Sie den Vertrag im Amoy-Block-Explorer aufrufen, werden Sie feststellen, dass der Wert von Zahl ist jetzt 9!
- Terminalausgabe
- Polygonscan als Proxy lesen
> npx hardhat Skripte ausführen/upgrade.ts --Netzwerk Amoy
Kompilieren Ihrer Solidity-Verträge...
Kompiliert 4 Solidity-Dateien mit solc 0.8.22 (EVM-Ziel: Shanghai)
Leistungsbilanz: 0x779fE50d4A9d0Eb760dD0d315b91d6A5CF265FCC
Upgrade auf V2...
Aktualisierung auf V2
Aktueller Wert nach dem Upgrade und dem Aufruf: 9

Bitte beachten Sie, dass nur das Konto, das die Proxy-Verträge bereitgestellt hat, die Upgrade-Funktion aufrufen kann – und das aus offensichtlichen Gründen. Das Transparenter, erweiterbarer Proxy Der Vertrag verweist nun auf die Adresse des neu bereitgestellten V2-Vertrags. Sehen Sie sich das folgende Flussdiagramm an:
Flussdiagramm für aktualisierbare Smart Contracts
Bitte beachten Sie, dass die Adresse des Benutzers, der eine bestimmte Funktion aufruft (msg.sender) ist entscheidend. Die Adresse bestimmt den gesamten Logikablauf.
Ist „msg.sender“ ein anderer Benutzer als der Administrator, delegiert der Proxy-Vertrag den Aufruf einfach an den Implementierungsvertrag, und die entsprechende Funktion wird ausgeführt. Somit ruft der Proxy-Vertrag die entsprechende Funktion aus dem Implementierungsvertrag im Namen von „msg.sender“, dem Endbenutzer, auf. Wie bereits erläutert, ist der Zustand des Implementierungsvertrags bedeutungslos, da er sich nicht ändert. Was sich hingegen ändert, ist der Zustand des Proxy-Vertrags, der auf der Grundlage dessen bestimmt wird, was vom Implementierungsvertrag bei der Ausführung der angeforderten Funktion zurückgegeben wird.
Das bedeutet: Ist der Aufrufer kein Administrator, wird der Proxy-Vertrag die Ausführung jeglicher Upgrade-Funktionen gar nicht erst in Betracht ziehen. Ist der Aufrufer kein Administrator, wird der Aufruf weitergeleitet oder delegiert unverzüglich mit der Umsetzung des Vertrags zu beginnen. Dies wird als Aufruf delegieren und ist ein wichtiges Konzept, das man verstehen muss. Ist der Aufrufer jedoch der Administrator, in diesem Fall unser ProxyAdmin Vertrag: Der Aufruf wird nicht automatisch delegiert, und es können alle Funktionen des Proxy-Vertrags ausgeführt werden, einschließlich der Upgrade-Funktion.
Überprüfung des Upgrades
Kehren Sie nun zum Amoy Block Explorer zurück und suchen Sie nach Ihrem Transparenter, erweiterbarer Proxy Vertragsadresse. Gehen Sie zur Vertrag Registerkarte und klicken Sie auf Als Bevollmächtigter schreiben. Sie sollten nun das neue V2 Adresse des Implementierungsvertrags, gefolgt von der vorherigen V1 Implementierungsadresse. Außerdem wird die Rückgang Funktion steht nun zur Verfügung. Das zeigt uns, dass der Proxy-Vertrag nun auf den neuen Implementierungsvertrag verweist! Hier ist ein Vorher-Nachher-Vergleich:
- Vor dem Upgrade
- Nach dem Upgrade


Das war’s. Du hast gerade einen aktualisierbaren Smart Contract bereitgestellt und ihn anschließend um eine neue Funktion erweitert. Lade den Code nun auf GitHub hoch und zeig ihn allen! Noch ein letzter Hinweis: Erinnerst du dich, wie wir eine .env-Datei verwendet haben, um unsere sensiblen Daten zu speichern? Der Zweck dieser Datei war es, zu verhindern, dass unsere sensiblen Daten öffentlich veröffentlicht werden und dadurch unsere Vermögenswerte auf der Blockchain gefährdet werden. Nachdem du überprüft hast, dass der Name der .env-Datei in deiner .gitignore-Datei aufgeführt ist, kannst du deinen Code ohne Bedenken auf GitHub hochladen, da sich keine privaten Daten in deiner hardhat.config.ts-Datei befinden.
Fazit
Klopft euch selbst auf die Schulter. Ihr habt es euch verdient. Das war ein ziemlich fortgeschrittenes Tutorial, und wenn ihr es sorgfältig befolgt habt, wisst ihr nun, wie man mithilfe der OpenZeppelin-Bibliothek einen einfachen, aktualisierbaren Smart Contract bereitstellt.
Abonniere unseren Newsletter, um weitere Artikel und Anleitungen zu Polygon zu erhalten. Wenn du Feedback hast, kannst du dich gerne über X an uns wenden. Du kannst dich jederzeit auf unserem Discord-Community-Server mit uns unterhalten – dort findest du einige der coolsten Entwickler, die du je treffen wirst 😊
Wir ❤️ Feedback!
Wenn Sie Anregungen oder Fragen zu dieser Dokumentation haben, teilen Sie uns diese bitte mit. Wir freuen uns auf Ihre Rückmeldung!