用于部署和管理可升级合约的 TronBox package TronBox 兼容该包迁移和测试代理相关的功能,因此使用 TronBox 可以为你的合约部署代理。
安装
npm install --save-dev @openzeppelin/truffle-upgrades
在 TronBox 中使用 @openzeppelin/truffle-upgrades
需要 TronBox V3.2.0 或之后的版本。
在 migrations 中使用
部署代理
若要在你的 migrations 中部署一个可升级的合约模式,可以使用 deployProxy
函数。 deployProxy
支持 UUPS(Universal Upgradeable Proxy Standard)
和 transparent
模式的代理。
注意:为了适配
deployProxy
函数,请先设置deployer.trufflePlugin
为true
。
// migrations/NN_deploy_upgradeable_box.js
const { deployProxy } = require('@openzeppelin/truffle-upgrades');
const Box = artifacts.require('Box');
module.exports = async function (deployer) {
try {
// Setup tronbox deployer
deployer.trufflePlugin = true;
const instance = await deployProxy(Box, [42], { deployer });
console.info('Deployed', instance.address);
// Call proxy contract
const box = await Box.deployed();
const beforeValue = await box.value();
console.info('Value before', beforeValue.toNumber());
// Set new Value
await box.setValue(beforeValue.toNumber() + 100);
const afterValue = await box.value();
console.info('Value after', afterValue.toNumber());
} catch (error) {
console.error('Transparent: deploy box error', error);
}
};
deployProxy
会先自动检查 Box
合约是否可以安全升级,接着为 Box
部署一个实现合约(除非在之前的部署中已经有一个),然后再创建部署一个代理合约,并通过调用 initialize(42)
去初始化它。如果是 transparent
模式的代理,还会创建部署一个代理管理员合约(ProxyAdmin)。
Beacon 模式代理部署
Beacon 代理模式允许多个代理合约通过引用 Beacon 合约来共享同一个逻辑实现。TronBox 也兼容了Beacon 代理模式。所以,可以使用这个插件去部署 Beacon 模式的代理合约。
deployBeacon
用于部署一个 Beacon 管理合约,这是一个用于管理多个实现了相同接口的合约的中心合约。
而 deployBeaconProxy
用于部署一个或多个 Beacon 代理合约,并将其指向 Beacon 合约,以实现升级和维护服务。
注意:为了适配
deployBeacon
和deployBeaconProxy
函数,请先设置deployer.trufflePlugin
为true
。
// migrations/NN_deploy_upgradeable_box.js
const { deployBeacon, deployBeaconProxy } = require('@openzeppelin/truffle-upgrades');
const Box = artifacts.require('Box');
module.exports = async function (deployer) {
try {
// Setup tronbox deployer
deployer.trufflePlugin = true;
const beacon = await deployBeacon(Box, { deployer });
console.info('Beacon deployed', beacon.address);
const instance = await deployBeaconProxy(beacon, Box, [42], { deployer });
console.info('Deployed', instance.address);
// Call proxy contract
const box = await Box.deployed();
const beforeValue = await box.value();
console.info('Value before', beforeValue.toNumber());
// Set new Value
await box.setValue(beforeValue.toNumber() + 100);
const afterValue = await box.value();
console.info('Value after', afterValue.toNumber());
} catch (error) {
console.error('Beacon: deploy box error', error);
}
};
关于 upgradeProxy
TronBox 暂未兼容使用 upgradeProxy
函数将部署的实例升级到一个新的版本。如果需要升级代理合约的实例,可以参考以下的示例。
部署新的 BoxV2 实现合约,并将现有代理升级到新的实现。
UUPS
// migrations/MM_upgrade_uups_box.js
const TransparentUpgradeableProxy = artifacts.require(
'@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'
);
const Box = artifacts.require('UUPSBox');
const BoxV2 = artifacts.require('UUPSBoxV2');
module.exports = async function (deployer) {
try {
// Deploy the new BoxV2 implementation contract
await deployer.deploy(BoxV2);
// Upgrade proxy contract
const proxyContract = await TransparentUpgradeableProxy.at(Box.address);
await proxyContract.upgradeTo(BoxV2.address);
console.info('Upgraded', Box.address);
// Call proxy contract
const box = await BoxV2.at(Box.address);
const beforeValue = await box.value();
console.info('Value before', beforeValue.toNumber());
// Set new Value
await box.setValue(beforeValue.toNumber() + 100);
const afterValue = await box.value();
console.info('Value after', afterValue.toNumber());
// Read new V2 Value
const beforeValueV2 = await box.valueV2();
console.info('ValueV2 before', beforeValueV2.toNumber());
// Set new V2 Value
await box.setValueV2(beforeValueV2.toNumber() + 100);
const afterValueV2 = await box.valueV2();
console.info('ValueV2 after', afterValueV2.toNumber());
} catch (error) {
console.error('UUPS: upgrade box error', error);
}
};
Transparent
// migrations/MM_upgrade_transparent_box.js
const { admin } = require('@openzeppelin/truffle-upgrades');
const ProxyAdmin = artifacts.require(
'@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'
);
const Box = artifacts.require('TransparentBox');
const BoxV2 = artifacts.require('TransparentBoxV2');
module.exports = async function (deployer) {
try {
// Deploy the new BoxV2 implementation contract
await deployer.deploy(BoxV2);
// Upgrade proxy contract by admin
const adminIns = await admin.getInstance();
const adminContract = await ProxyAdmin.at(adminIns.address);
await adminContract.upgrade(Box.address, BoxV2.address);
console.info('Upgraded', Box.address);
// Call proxy contract
const box = await BoxV2.at(Box.address);
const beforeValue = await box.value();
console.info('Value before', beforeValue.toNumber());
// Set new Value
await box.setValue(beforeValue.toNumber() + 100);
const afterValue = await box.value();
console.info('Value after', afterValue.toNumber());
// Read new V2 Value
const beforeValueV2 = await box.valueV2();
console.info('ValueV2 before', beforeValueV2.toNumber());
// Set new V2 Value
await box.setValueV2(beforeValueV2.toNumber() + 100);
const afterValueV2 = await box.valueV2();
console.info('ValueV2 after', afterValueV2.toNumber());
} catch (error) {
console.error('Transparent: upgrade box error', error);
}
};
Beacon
当 Beacon 被升级时,所有指向它的 Beacon 代理都会使用新的合约实现。
// migrations/MM_upgrade_beacon_box.js
const UpgradeableBeacon = artifacts.require(
'@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json'
);
const { erc1967 } = require('@openzeppelin/truffle-upgrades');
const Box = artifacts.require('BeaconBox');
const BoxV2 = artifacts.require('BeaconBoxV2');
module.exports = async function (deployer) {
try {
// Deploy the new BoxV2 implementation contract
await deployer.deploy(BoxV2);
const beaconAddress = await erc1967.getBeaconAddress(Box.address);
// Upgrade proxy contract
const proxyContract = await UpgradeableBeacon.at(beaconAddress);
await proxyContract.upgradeTo(BoxV2.address);
console.info('Upgraded', Box.address);
// Call proxy contract
const box = await BoxV2.at(Box.address);
const beforeValue = await box.value();
console.info('Value before', beforeValue.toNumber());
// Set new Value
await box.setValue(beforeValue.toNumber() + 100);
const afterValue = await box.value();
console.info('Value after', afterValue.toNumber());
// Read new V2 Value
const beforeValueV2 = await box.valueV2();
console.info('ValueV2 before', beforeValueV2.toNumber());
// Set new V2 Value
await box.setValueV2(beforeValueV2.toNumber() + 100);
const afterValueV2 = await box.valueV2();
console.info('ValueV2 after', afterValueV2.toNumber());
} catch (error) {
console.error('Beacon: upgrade box error', error);
}
};