Как создать обновляемый смарт-контракт в Celo


Что такое обновляемые смарт-контракты?

Обычно, когда мы развертываем смарт-контракт, невозможно обновить или изменить код, поскольку он находится на цепи, и так и должно быть. Это повышает безопасность и доверие пользователей, которые взаимодействуют с этим контрактом.

Но могут быть случаи, когда вы хотите обновить свой смарт-контракт, например, исправить серьезные ошибки или добавить некоторые критически важные функции для пользователей. Традиционно сделать это невозможно. Максимум, что можно сделать, — это создать новый смарт-контракт с исправлением ошибок и переносом всей информации. На этом работа не заканчивается: необходимо обновить ссылки, где использовался адрес старого контракта, и проинформировать существующих пользователей об использовании нового контракта.

Это может оказаться непосильной задачей, но есть способ справиться с такими случаями с помощью обновляемых контрактов. Обновляемый смарт-контракт можно обновлять и редактировать после развертывания. Этого можно достичь с помощью плагина/инструмента, созданного OpenZeppelin.

В двух словах

Плагин используется для развертывания контрактов на Hardhat или truffle. Если в будущем вы захотите обновить смарт-контракт, используйте тот же адрес, который вы использовали для развертывания первого контракта с помощью плагина, и плагин обработает передачу любого состояния и данных из старого контракта, сохраняя тот же адрес контракта для взаимодействия.

OpenZeppelin использует модель прокси, где они развертывают три смарт-контракта для управления уровнем хранения и реализации смарт-контрактов. Каждый раз, когда вызывается контракт, пользователь косвенно вызывает прокси-контракт, а прокси-контракт передает параметры реализованному смарт-контракту, прежде чем отправить результат обратно пользователю. Теперь, поскольку у нас есть прокси-контракт в качестве посредника, представьте, что вы хотите изменить что-то в своем реализованном контракте. Все, что вам нужно сделать, это развернуть новый контракт и сказать своему прокси-контракту ссылаться на последний смарт-контракт, и вуаля. Все пользователи используют обновленный контракт.

Как работают обновляемые контракты?

Когда мы используем обновляемый плагин OpenZeppelin для развертывания контракта, развертываются три контракта —

  • Внедренный контракт — контракт, который создают разработчики и который содержит всю логику и функциональные возможности.
  • Прокси-контракт — контракт, с которым взаимодействует конечный пользователь. Все данные и состояние контракта хранятся в контексте прокси-контракта. Этот прокси-контракт является реализацией стандарта EIP1967.
  • Контракт ProxyAdmin — Этот контракт связывает контракт Proxy и контракт реализации.

Что такое ProxyAdmin? (Согласно документации OpenZeppelin)
ProxyAdmin — это контракт, который действует как владелец всех ваших прокси. В каждой сети развертывается только один. Когда вы запускаете свой проект, ProxyAdmin принадлежит адресу deployer, но вы можете передать право собственности на него, вызвав transferOwnership.

Когда пользователь вызывает контракт прокси, вызов делегируется контракту реализации. Теперь, чтобы обновить контракт, нам нужно сделать следующее:

  1. Развернуть обновленный контракт реализации.
  2. Обновление ProxyAdmin таким образом, чтобы все вызовы перенаправлялись на новый внедренный контракт.

OpenZeppelin создал плагин для Hardhat и Truffle, чтобы справиться с этими задачами за нас. Давайте рассмотрим шаг за шагом, как создавать и тестировать обновляемые контракты.

Написание обновляемых смарт-контрактов

Мы будем использовать локальный хост Hardhat для локального тестирования контракта и тестовую сеть Celo Alfajores.

Инициализируйте проект и установите зависимости

mkdir upgradeable-contract && cd upgradeable-contract
yarn init -y
yarn add hardhat
yarn hardhat // choose typescript
Войдите в полноэкранный режим Выйти из полноэкранного режима

Добавьте плагин и обновите hardhat.config.ts

yarn add @openzeppelin/hardhat-upgrades
// hardhat.config.ts
import '@openzeppelin/hardhat-upgrades';
Войдите в полноэкранный режим Выйти из полноэкранного режима

Для понимания процесса мы будем использовать контракт Greeter.sol. Мы создадим и развернем несколько версий этого контракта.

  • Greeter.sol
  • GreeterV2.sol
  • GreeterV3.sol

ПРИМЕЧАНИЕ — Большая разница между обычным контрактом и обновляемым смарт-контрактом заключается в том, что обновляемые смарт-контракты не имеют конструктора.

//contracts/Greeter.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract Greeter {
    string public greeting;
    // Emitted when the stored value changes
    event ValueChanged(string newValue);
    function greet() public view returns (string memory) {
        return greeting;
    }
    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
        emit ValueChanged(_greeting);
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Это очень простой контракт Greeter, который возвращает значение greeting всякий раз, когда мы вызываем метод greet().

Юнит-тестирование для Greeter.sol

Создайте файл с именем 1.Greeter.test.ts и добавьте в него следующее содержимое.

// test/1.Greeter.test.ts
import { expect } from "chai";
import { Contract } from "ethers";
import { ethers } from "hardhat";
describe("Greeter", function () {
  let greeter: Contract;
beforeEach(async function () {
    const Greeter = await ethers.getContractFactory("Greeter");
    greeter = await Greeter.deploy();
    await greeter.deployed();
  });
it("should greet correctly before and after changing value", async function () {
    await greeter.setGreeting("Celo to the Moon");
    expect(await greeter.greet()).to.equal("Celo to the Moon");
  });
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Запустите тест:

yarn hardhat test test/1.Greeter.test.ts
Войти в полноэкранный режим Выйти из полноэкранного режима

Результаты:

Greeter
    ✔ should greet correctly before and after changing value
1 passing (334ms)
✨  Done in 1.73s.
Войти в полноэкранный режим Выход из полноэкранного режима

Давайте напишем сценарий развертывания и развернем Greeter.sol на локальном узле Hardhat. Создайте файл с именем Greeter.deploy.ts в каталоге scripts и вставьте в него следующий код.

// scripts/Greeter.deploy.ts
import { ethers, upgrades } from "hardhat";
async function main() {
  const Greeter = await ethers.getContractFactory("Greeter");
  console.log("Deploying Greeter...");
  const greeter = await upgrades.deployProxy(Greeter);
console.log(greeter.address, " greeter(proxy) address");
  console.log(
    await upgrades.erc1967.getImplementationAddress(greeter.address),
    " getImplementationAddress"
  );
  console.log(
    await upgrades.erc1967.getAdminAddress(greeter.address),
    " getAdminAddress"
  );
}
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Запустите локальный узел и разверните контракт Greeter.sol:

yarn hardhat node
yarn hardhat run scripts/Greeter.deploy.ts --network localhost
Войти в полноэкранный режим Выйти из полноэкранного режима

Результаты:

Deploying Greeter...
0x9A676e781A523b5d0C0e43731313A708CB607508  greeter(proxy) address
0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0  getImplementationAddress
0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82  getAdminAddress
✨  Done in 2.74s.
Войти в полноэкранный режим Выход из полноэкранного режима

Примечание: Если выполнить команду развертывания несколько раз, можно заметить, что adminAddress не меняется.

Теперь нам нужно реализовать функцию increment в существующем контракте. Вместо замены текущего контракта мы создадим новый контракт и изменим прокси, чтобы он ссылался на новый контракт.

Создание GreeterV2.sol

// contracts/GreeterV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Greeter.sol";
contract GreeterV2 is Greeter {
    uint256 public counter;
    // Increments the counter value by 1
    function increment() public {
        counter++;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Написание модульных тестов для GreeterV2 перед развертыванием.

import { expect } from "chai";
import { BigNumber, Contract } from "ethers";
import { ethers } from "hardhat";
describe("Greeter V2", function () {
  let greeterV2: Contract;
beforeEach(async function () {
    const GreeterV2 = await ethers.getContractFactory("GreeterV2");
    greeterV2 = await GreeterV2.deploy();
    await greeterV2.deployed();
  });
it("should retrieve value previously stored", async function () {
    await greeterV2.setGreeting("Celo to the Moon");
    expect(await greeterV2.greet()).to.equal("Celo to the Moon");
  });
it("should increment value correctly", async function () {
    expect(await greeterV2.counter()).to.equal(BigNumber.from("0"));
    await greeterV2.increment();
    expect(await greeterV2.counter()).to.equal(BigNumber.from("1"));
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Запуск теста —

yarn hardhat test test/2.GreeterV2.test.ts
Войти в полноэкранный режим Выход из полноэкранного режима

Результаты —

Greeter V2
    ✔ should retrieve value previously stored
    ✔ should increment value correctly
2 passing (442ms)
✨  Done in 6.43s.
Войти в полноэкранный режим Выход из полноэкранного режима

Приведенный выше тест был разработан только для проверки GreeterV2.sol, а не обновленной версии Greeter.sol. Давайте напишем и развернем GreeterV2 в proxy patter и проверим, правильно ли работает GreeterV2.

// test/3.GreeterV2Proxy.test.ts
import { expect } from "chai";
import { Contract } from "ethers";
import { ethers, upgrades } from "hardhat";
describe("Greeter (proxy) V2", function () {
  let greeter: Contract;
  let greeterV2: Contract;
beforeEach(async function () {
    const Greeter = await ethers.getContractFactory("Greeter");
    const GreeterV2 = await ethers.getContractFactory("GreeterV2");
greeter = await upgrades.deployProxy(Greeter);
// setting the greet value so that it can be checked after upgrade
    await greeter.setGreeting("WAGMI");
greeterV2 = await upgrades.upgradeProxy(greeter.address, GreeterV2);
  });
it("should retrieve value previously stored correctly", async function () {
    expect(await greeterV2.greet()).to.equal("WAGMI");
await greeter.setGreeting("Celo to the Moon");
    expect(await greeterV2.greet()).to.equal("Celo to the Moon");
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь мы устанавливаем значение greeting на WAGMI в Greeter (V1) и после обновления проверяем значение greeting в GreeterV2.

Запустите тест —

yarn hardhat test test/3.GreeterV2Proxy.test.ts
Войдите в полноэкранный режим Выйдите из полноэкранного режима

Результаты —

Greeter (proxy) V2
    ✔ should retrieve value previously stored correctly
1 passing (521ms)
✨  Done in 6.45s.
Вход в полноэкранный режим Выход из полноэкранного режима

Написание скрипта для обновления Greeter до GreeterV2.

Обратите внимание, что прокси адрес greeter — 0x9A676e781A523b5d0C0e43731313A708CB607508, который мы получили при развертывании Greeter.sol. Нам нужен прокси адрес для развертывания GreeterV2.sol.

Создайте файл GreeterV2.deploy.ts и добавьте следующий код.

// scripts/2.upgradeV2.ts
import { ethers, upgrades } from "hardhat";
const proxyAddress = "0x9A676e781A523b5d0C0e43731313A708CB607508";
async function main() {
  console.log(proxyAddress, " original Greeter(proxy) address");
  const GreeterV2 = await ethers.getContractFactory("GreeterV2");
  console.log("upgrade to GreeterV2...");
  const greeterV2 = await upgrades.upgradeProxy(proxyAddress, GreeterV2);
  console.log(greeterV2.address, " GreeterV2 address(should be the same)");
console.log(
    await upgrades.erc1967.getImplementationAddress(greeterV2.address),
    " getImplementationAddress"
  );
  console.log(
    await upgrades.erc1967.getAdminAddress(greeterV2.address),
    " getAdminAddress"
  );
}
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
Вход в полноэкранный режим Выход из полноэкранного режима

Запуск скрипта развертывания для GreeterV2.sol.

Нам нужно начать с самого начала, т.е. сначала развернуть Greeter.sol, получить адрес прокси и использовать его для развертывания GreeterV2.sol.

yarn hardhat node
yarn hardhat run scripts/Greeter.deploy.ts --network localhost
Вход в полноэкранный режим Выйдите из полноэкранного режима

Здесь мы получили 0x9A676e781A523b5d0C0e43731313A708CB607508 в качестве адреса прокси. (Обновите адрес прокси в scripts/GreeterV2.deploy.ts).

yarn hardhat run scripts/GreeterV2.deploy.ts --network localhost
Вход в полноэкранный режим Выход из полноэкранного режима

Результаты —

0x9A676e781A523b5d0C0e43731313A708CB607508  original Greeter(proxy) address
upgrade to GreeterV2...
0x9A676e781A523b5d0C0e43731313A708CB607508  GreeterV2 address(should be the same)
0x0B306BF915C4d645ff596e518fAf3F9669b97016  getImplementationAddress
0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82  getAdminAddress
✨  Done in 2.67s.
Вход в полноэкранный режим Выход из полноэкранного режима

Переопределение существующих методов

Теперь предположим, что вы хотите добавить пользовательское поле имени в приветствие, чтобы при вызове greet() имя добавлялось в возвращаемую строку.

Создайте новый файл GreeterV3.sol в директории contracts и добавьте следующий код.

// contracts/GreeterV3.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./GreeterV2.sol";
contract GreeterV3 is GreeterV2 {
    string public name;
    function setName(string memory _name) public {
        name = _name;
    }
    function greet() public view override returns (string memory) {
        return string(abi.encodePacked(greeting, " ", name));
    }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Давайте напишем тестовые примеры для развертывания и тестирования GreeterV3 . Создайте файл 4.GreeterV3Proxy.test.ts и добавьте следующие тестовые случаи.

// test/.GreeterV3Proxy.test.ts
import { expect } from "chai";
import { BigNumber, Contract } from "ethers";
import { ethers, upgrades } from "hardhat";
describe("Greeter (proxy) V3 with name", function () {
  let greeter: Contract;
  let greeterV2: Contract;
  let greeterV3: Contract;
beforeEach(async function () {
    const Greeter = await ethers.getContractFactory("Greeter");
    const GreeterV2 = await ethers.getContractFactory("GreeterV2");
    const GreeterV3 = await ethers.getContractFactory("GreeterV3");
    greeter = await upgrades.deployProxy(Greeter);
// setting the greet value so that it can be checked after upgrade
    await greeter.setGreeting("WAGMI");
greeterV2 = await upgrades.upgradeProxy(greeter.address, GreeterV2);
    greeterV3 = await upgrades.upgradeProxy(greeter.address, GreeterV3);
  });
it("should retrieve value previously stored and increment correctly", async function () {
    expect(await greeterV2.greet()).to.equal("WAGMI ");
    expect(await greeterV3.counter()).to.equal(BigNumber.from("0"));
    await greeterV2.increment();
    expect(await greeterV3.counter()).to.equal(BigNumber.from("1"));
  });
it("should set name correctly in V3", async function () {
    expect(await greeterV3.name()).to.equal("");
    const name = "Viral";
    await greeterV3.setName(name);
    expect(await greeterV3.name()).to.equal(name);
    expect(await greeterV3.greet()).to.equal(`WAGMI ${name}`);
  });
});
Вход в полноэкранный режим Выход из полноэкранного режима

Примечание 1 — Поскольку все данные и состояние хранятся в контракте Proxy, вы можете видеть в тестовых примерах, что мы вызываем метод increment() в GreeterV2 и проверяем, увеличилось ли значение или нет в GreeterV3.

Примечание 2 — Поскольку в нашем GreeterV3.sol контракте мы добавили пробел и имя к greeting Если имя равно null, то вызов greet вернет greeting с добавленным к нему пробелом.

Запустите тесты —

yarn hardhat test test/4.GreeterV3Proxy.test.ts
Войти в полноэкранный режим Выход из полноэкранного режима

Результаты —

Greeter (proxy) V3 with name
    ✔ should retrieve value previously stored and increment correctly
    ✔ should set name correctly in V3
2 passing (675ms)
✨  Done in 2.38s.
Вход в полноэкранный режим Выход из полноэкранного режима

Сценарий развертывания для GreeterV3.sol

Создайте файл с именем GreeterV3.deploy.ts в scripts и вставьте в него следующий код —

// scripts/3.upgradeV3.ts
import { ethers, upgrades } from "hardhat";
const proxyAddress = "0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1";
async function main() {
  console.log(proxyAddress, " original Greeter(proxy) address");
  const GreeterV3 = await ethers.getContractFactory("GreeterV3");
  console.log("upgrade to GreeterV3...");
  const greeterV3 = await upgrades.upgradeProxy(proxyAddress, GreeterV3);
  console.log(greeterV3.address, " GreeterV3 address(should be the same)");
console.log(
    await upgrades.erc1967.getImplementationAddress(greeterV3.address),
    " getImplementationAddress"
  );
  console.log(
    await upgrades.erc1967.getAdminAddress(greeterV3.address),
    " getAdminAddress"
  );
}
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Разверните GreeterV3.

yarn hardhat run scripts/GreeterV3.deploy.ts --network localhost
Войти в полноэкранный режим Выйти из полноэкранного режима

Примечание — Если вы получите сообщение «Ошибка: Proxy admin is not the one registered in the network manifest» при попытке развернуть GreeterV3, вам нужно снова запустить Greeter.deploy.ts и GreeterV2.deploy.ts и скопировать новый адрес прокси, который будет использоваться для развертывания обновленных контрактов.

Результаты —

0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1  original Greeter(proxy) address
upgrade to GreeterV3...
0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1  GreeterV3 address(should be the same)
0x3Aa5ebB10DC797CAC828524e59A333d0A371443c  getImplementationAddress
0x59b670e9fA9D0A427751Af201D676719a970857b  getAdminAddress
✨  Done in 2.63s.
Вход в полноэкранный режим Выход из полноэкранного режима

Развертывание обновленного контракта вручную

Давайте напишем новый контракт — GreeterV4, где нам нужно —

  • Изменить переменную состояния name с public на private.
  • Добавить метод getName для получения значения name.

Создайте файл GreeterV4.sol в директории contracts и добавьте следующий код.

// contracts/GreeterV4.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./GreeterV2.sol";
contract GreeterV4 is GreeterV2 {
    string private name;
event NameChanged(string name);
function setName(string memory _name) public {
        name = _name;
    }
function getName() public view returns (string memory) {
        return name;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь нам нужно наследоваться от GreeterV2 вместо GreeterV3, потому что GreeterV3 уже имеет переменную состояния name и мы не можем изменить видимость, но что мы можем сделать, так это наследоваться от GreeterV3, который не имеет переменной name и установить видимость так, как мы хотим.

Давайте напишем тестовые примеры для GreeterV4.sol.

// test/5.GreeterV4Proxy.test.ts
/* eslint-disable no-unused-vars */
import { expect } from "chai";
import { Contract } from "ethers";
import { ethers, upgrades } from "hardhat";
describe("Greeter (proxy) V4 with getName", function () {
  let greeter: Contract;
  let greeterV2: Contract;
  let greeterV3: Contract;
  let greeterV4: Contract;
beforeEach(async function () {
    const Greeter = await ethers.getContractFactory("Greeter");
    const GreeterV2 = await ethers.getContractFactory("GreeterV2");
    const GreeterV3 = await ethers.getContractFactory("GreeterV3");
    const GreeterV4 = await ethers.getContractFactory("GreeterV4");
greeter = await upgrades.deployProxy(Greeter);
    greeterV2 = await upgrades.upgradeProxy(greeter.address, GreeterV2);
    greeterV3 = await upgrades.upgradeProxy(greeter.address, GreeterV3);
    greeterV4 = await upgrades.upgradeProxy(greeter.address, GreeterV4);
  });
it("should setName and getName correctly in V4", async function () {
    expect(await greeterV4.getName()).to.equal("");
const greetername = "Celo";
    await greeterV4.setName(greetername);
    expect(await greeterV4.getName()).to.equal(greetername);
  });
});
Вход в полноэкранный режим Выйдите из полноэкранного режима

Запустите тест —

yarn hardhat test test/5.GreeterV4Proxy.test.ts
Войти в полноэкранный режим Выйти из полноэкранного режима

Результаты —

Greeter (proxy) V4 with getName
    ✔ should setName and getName correctly in V4
1 passing (608ms)
✨  Done in 6.18s.
Войти в полноэкранный режим Выход из полноэкранного режима

Подготовка к обновлению, но не настоящее обновление

Когда мы вызываем upgrades.upgradeProxy(), происходит несколько вещей —

  • Сначала разворачивается ваш контракт,
  • ProxyAdmin вызывает метод upgrade() и связывает прокси с адресом нового внедренного контракта.

Чтобы выполнить эти шаги вручную, мы можем вызвать upgrades.prepareUpgrade(), который только развернет ваш контракт, но не свяжет его с прокси YET. Разработчики могут связать его вручную. Это полезно, когда вы хотите протестировать в продакшене, прежде чем все пользователи захотят использовать новый контракт.

Создайте файл GreeterV4Prepare.deploy.ts в директории scripts и добавьте следующий код.

import { ethers, upgrades } from "hardhat";
const proxyAddress = "0xc5a5C42992dECbae36851359345FE25997F5C42d";
async function main() {
  console.log(proxyAddress, " original Greeter(proxy) address");
  const GreeterV4 = await ethers.getContractFactory("GreeterV4");
  console.log("Preparing upgrade to GreeterV4...");
  const greeterV4Address = await upgrades.prepareUpgrade(
    proxyAddress,
    GreeterV4
  );
  console.log(greeterV4Address, " GreeterV4 implementation contract address");
}
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Примечание — Вам может понадобиться запустить все сценарии развертывания снова, если вы столкнетесь с какими-либо проблемами при выполнении этого сценария.

Запустите сценарий —

yarn hardhat run scripts/GreeterV4Prepare.deploy.ts --network localhost
Войти в полноэкранный режим Выйти из полноэкранного режима

Результаты —

0xc5a5C42992dECbae36851359345FE25997F5C42d  original Greeter(proxy) address
Preparing upgrade to GreeterV4...
0x9E545E3C0baAB3E08CdfD552C960A1050f373042  GreeterV4 implementation contract address
✨  Done in 2.56s.
Вход в полноэкранный режим Выйти из полноэкранного режима

Развертывание всех контрактов в сети Alfajores компании Celo

Сначала нам нужно добавить детали RPC Alfajores в hardhat.config.ts. Обратитесь к этому сайту для получения последней информации о RPC — https://docs.celo.org/getting-started/wallets/using-metamask-with-celo/manual-setup.

alfajores: {
      url: "https://alfajores-forno.celo-testnet.org",
      accounts:
        process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
    },
Войдите в полноэкранный режим Выйти из полноэкранного режима

Запустите скрипт и разверните Greeter.sol.

Для справки, вот три развернутых контракта —

Обновление Greeter до GreeterV2.

Выполните следующую команду для развертывания GreeterV2. Перед запуском обновите proxyAddress в GreeterV2.deploy.ts на адрес контракта развернутого прокси.

yarn hardhat run scripts/GreeterV2.deploy.ts --network alfajores
Вход в полноэкранный режим Выход из полноэкранного режима

Поскольку мы вызвали upgrades.upgradeProxy в сценарии развертывания, он сделал две вещи, развернул новый контракт реализации и вызвал proxyAdmin.upgrade() метод для связи прокси и нового контракта реализации.

Обновление GreeterV2 до GreeterV3.

Выполните следующую команду —

yarn hardhat run scripts/GreeterV3.deploy.ts --network alfajores
Войти в полноэкранный режим Выйти из полноэкранного режима

Обновление GreeterV3 до GreeterV4.

В GreeterV4Prepare.deploy.ts мы вызвали только prepareUpgrade, который только развертывает контракт реализации. Мы будем использовать консоль hardhat, чтобы вручную вызвать update() метод для связывания прокси с контрактом реализации. Выполните следующую команду, чтобы развернуть контракт реализации.

yarn hardhat run scripts/GreeterV4Prepare.deploy.ts --network alfajores
Вход в полноэкранный режим Выйдите из полноэкранного режима

Результаты —

0xd15100A570158b7EEbE196405D8a456d56807F2d  original Greeter(proxy) address
Preparing upgrade to GreeterV4...
0x8843F73D7c761D29649d4AC15Ee9501de12981c3  GreeterV4 implementation contract address
✨  Done in 5.81s.
Вход в полноэкранный режим Выход из полноэкранного режима

Мы будем использовать консоль hardhat для связи прокси с контрактом реализации. Выполните следующую команду, чтобы запустить консоль.

yarn hardhat console --network alfajores
Войти в полноэкранный режим Выйти из полноэкранного режима

Для связывания нам понадобятся две вещи —

  • Адрес ProxyAdmin

Выполните следующие команды в консоли —

> const GreeterV4 = await ethers.getContractFactory("GreeterV4");
> await upgrades.upgradeProxy("0xd15100A570158b7EEbE196405D8a456d56807F2d", GreeterV4);
Войти в полноэкранный режим Выйти из полноэкранного режима

Это укажет прокси контракт на последнюю версию GreeterV4.

Заключение

Поздравляю 💪 с тем, что вы дошли до конца. Я знаю, что эта статья получилась длинноватой, но теперь вы знаете, как обновляемые смарт-контракты работают под капотом. Вы создали и развернули прокси-контракты в локальном тестнете и тестнете Alfajores. Если у вас есть сомнения или вы просто хотите сказать «Привет 👋🏻», вы можете связаться со мной в Twitter или Discord[0xViral (Celo)#6692].

  • Celo Discord
  • Celo Twitter

Оцените статью
devanswers.ru
Добавить комментарий