OpenZeppelinアップグレードプラグインを使用してデプロイされたコントラクトはアップグレード可能です。アドレス、状態、および残高を維持しながら、コードを変更するためにアップグレードできます。新しい機能をプロジェクトに繰り返し追加したり、ライブバージョンで見つかった可能性のあるバグを修正したりできます。
開発環境を構成します
新しいnpmプロジェクトを作成する
mkdir mycontract && cd mycontract
npm init -y
Truffleをインストールして初期化します
npm i --save-dev truffle
npx truffle init
Truffleアップグレードプラグインをインストールします
npm i --save-dev @openzeppelin/truffle-upgrades
アップグレード可能な契約を作成する
アップグレード可能なコントラクトは、コンストラクターではなく初期化関数を使用して状態を初期化することに注意してください。
Box.sol
// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract Box {
uint256 private value;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
}
契約をパブリックネットワークに展開する
Truffleの移行を使用して、Boxコントラクトをデプロイします。Truffleアップグレードプラグインは、アップグレード可能なコントラクトをデプロイするためのdeployProxy関数を提供します。実装したコントラクトをデプロイし、ProxyAdminがプロジェクトプロキシおよびプロキシ管理者として機能し、初期化関数を呼び出します。
移行ディレクトリに次の2_deploy_contracts.jsスクリプトを作成します。
この記事では、初期化関数をまだ持っていないので、ストア関数を使用して状態を初期化します。
2_deploy_contracts.js
// migrations/2_deploy_box.js
const Box = artifacts.require('Box');
const { deployProxy } = require('@openzeppelin/truffle-upgrades');
module.exports = async function (deployer) {
await deployProxy(Box, [42], { deployer, initializer: 'store' });
};
Rinkebyネットワークでtrufflemigrateを実行してデプロイします。Box.sol、ProxyAdmin、およびプロキシコントラクトTransparentUpgradeableProxyの3つのコントラクトを確認できます。
truffle migrate --network rinkeby
...
2_deploy_contracts.js
===============「ボックス」のデプロイ
---------------
>トランザクションハッシュ:0x1e5a61c2d4560d6ffe5cc60d7badbfef6d5e420708eebff6dc580bb3f9f9f3e1
>ブロック:1秒:14
>コントラクトアドレス:
0x7f7dc11961fCD81f53e9F45D9DfB745'ProxyAdmin'のデプロイ
----------------------
>トランザクションハッシュ:0x298b429391c5a98701bf79df00f4f5526c61570f3091b3d6693e3a4f12a88409
>ブロック:1秒:14
>コントラクトアドレス:0x7Bd40e62aEe2c5e232152351f57068038761E20F
.. ..'TransparentUpgradeableProxy'のデプロイ
---------------------------------------
>トランザクションハッシュ:0x7a0043dbe9a35ab9eab8cf0eac1856418cd0c359e330448df016150d293e6716
>ブロック:2秒:26
>契約アドレス:0xc2ea7DE43F194bB397761a30a05CEDcF28835F24
.. ..
検証契約を公開する
truffle run verify Box --network rinkeby
トリュフコンソールを使用して、契約を操作できます。
注:Box.deployed()は、プロキシコントラクトのアドレスです。
truffle console --network rinkeby
truffle(rinkeby)> box = await Box.deployed()
truffle(rinkeby)> box.address
'0xc2ea7DE43F194bB397761a30a05CEDcF28835F24'
truffle(rinkeby)>(await box.retrieve())。toString()
'42'
現在のプロキシの管理者(アップグレードを実行できる)は、ProxyAdminコントラクトです。ProxyAdminの所有者のみがプロキシをアップグレードできます。警告:ProxyAdminの所有権が譲渡されるときは、必ず私たちが管理するアドレスに移動してください。
移行ディレクトリに次の3_transfer_ownership.jsスクリプトを作成します。
3_transfer_ownership.js
// migrations/3_transfer_ownership.js
const { admin } = require('@openzeppelin/truffle-upgrades');
module.exports = async function (deployer, network) {
// 使用你的 钱包 地址
const admin = '0x1c14600daeca8852BA559CC8EdB1C383B8825906';
// Don't change ProxyAdmin ownership for our test network
if (network !== 'test') {
// The owner of the ProxyAdmin can upgrade our contracts
await admin.transferProxyAdminOwnership(admin);
}
};
Rinkebyネットワークで移行を実行します
truffle migrate --network rinkeby
...
3_transfer_ownership.js
=======================>移行をチェーンに保存します。
-------------------------------------..。
_
新しいアップグレードバージョンを実装する
しばらくして、契約に機能を追加することにしました。この記事では、インクリメント関数を追加します。
注:以前の契約実装のストレージレイアウトを変更することはできません。技術的制限の詳細については、アップグレードを参照してください。
次のSolidityコードを使用して、contractsディレクトリに新しい実装BoxV2.solを作成します。
BoxV2.sol
// contracts/BoxV2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract BoxV2 {
uint256 private value;
// Emitted when the stored value changes
event ValueChanged(uint256 newValue);
// Stores a new value in the contract
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
// Reads the last stored value
function retrieve() public view returns (uint256) {
return value;
}
// Increments the stored value by 1
function increment() public {
value = value + 1;
emit ValueChanged(value);
}
}
新しいアップグレードバージョンを展開します
新しい実装がテストされると、アップグレードの準備が整います。これにより、新しい契約が検証および展開されます。注:アップグレードの準備をしているだけです。
移行ディレクトリに次の4_prepare_upgrade_boxv2.jsスクリプトを作成します。
4_prepare_upgrade_boxv2.js
// migrations/4_prepare_upgrade_boxv2.js
const Box = artifacts.require('Box');
const BoxV2 = artifacts.require('BoxV2');
const { prepareUpgrade } = require('@openzeppelin/truffle-upgrades');
module.exports = async function (deployer) {
const box = await Box.deployed();
await prepareUpgrade(box.address, BoxV2, { deployer });
};
Rinkebyネットワークで移行を実行して、新しいコントラクト実装をデプロイします
truffle migrate --network rinkeby
...
4_prepare_upgrade_boxv2.js
=========================='BoxV2'のデプロイ
-----------------
>トランザクションハッシュ:0x078c4c4454bb15e3791bc80396975e6e8fc8efb76c6f54c321cdaa01f5b960a7
>ブロック:1秒:17
>コントラクトアドレス:0xEc784bE1CC7F5deA6976f61f578b328E856FB72c
...
展開後のアドレス
トリュフコンソールに入る
truffle console --network rinkeby
truffle(rinkeby)> box = await Box.deployed()
truffle(rinkeby)> boxV2 = await BoxV2.deployed()
truffle(rinkeby)> box.address
'0xF325bB49f91445F97241Ec5C286f90215a7E3BC6'
truffle(rinkeby)> boxV2.address
'0xE
アップグレード契約
ProxyAdminコントラクトのアップグレードメソッドを実行します
プロキシ:TransparentUpgradeableProxyコントラクトのアドレス
実装:BoxV2契約のアドレス
次に、トランザクションをMetaMask(または使用しているウォレット)で署名する必要があります。
これで、アップグレードされた契約を操作できます。BoxV2と対話するには、プロキシアドレスを使用する必要があります。次に、新しい「インクリメンタル」関数を呼び出して、アップグレード全体を通じて状態が維持されていることを確認できます。
トリュフコンソールに入る
truffle console --network rinkeby
truffle(rinkeby)> box = await Box.deployed()
truffle(rinkeby)> boxV2 = await BoxV2.at(box.address)
truffle(rinkeby)>(await boxV2.retrieve())。toString()
'42'
トリュフ(rinkeby)> await boxV2.increment()
{tx:
...
truffle(rinkeby)>(await boxV2.retrieve())。toString()
'43'