Migrate from OpenZeppelin CLI
This guide is for migrating from an old CLI which is deprecated. |
This guide will walk through migrating a project from the OpenZeppelin CLI to OpenZeppelin Upgrades Plugins for either Truffle or Hardhat.
Instructions are available for both Truffle and Hardhat. Choose your preference using this toggle! |
If you’d like to try out the instructions on a sample OpenZeppelin CLI project, you can clone OpenZeppelin/openzeppelin-upgrades-migration-example and follow the setup instructions in the readme before continuing. |
Differences
The main difference between the CLI and the plugins is that the former used to keep track of your upgradeable (and non-upgradeable) deployments for you. This was handy in some contexts since you didn’t need to worry too much about proxies, implementations or addresses and you could just focus on operations like upgrading or sending transactions to your contracts just by their name.
But having the CLI keep track of your deployments came at the expense of limiting your freedom to integrate with different tools and workflows. And since the plugins were designed to work independently of the CLI, we lifted that restriction so now you have the flexibility to keep track of your proxies the way you think it’s best.
Keeping that aside, everything else remains the same since both the CLI and plugins make use of the same known Proxy
and ProxyAdmin
contracts under the hood, making up two different interfaces to manage them. This means that migrating your project won’t touch anything on chain, everything is safe and local.
Installation
Install Hardhat and when initializing it, select the Create an empty hardhat.config.js
option.
$ npm install --save-dev hardhat
$ npx hardhat
Then install the Upgrades plugin:
$ npm install --save-dev @openzeppelin/hardhat-upgrades
$ npm install --save-dev @nomiclabs/hardhat-ethers ethers # peer dependencies
Once it is done, register the plugin in the Hardhat config file by adding these lines:
// hardhat.config.js
require('@openzeppelin/hardhat-upgrades');
module.exports = {
// ...
};
Install Truffle, and initialize your project.
Choose not to overwrite contracts or test directories when Truffle asks. By not overwriting, you won’t get an initial migration. Make sure you create Migrations.sol and the initial migration.
|
$ npm install --save-dev truffle
$ npx truffle init
Then install the Upgrades plugin:
$ npm install --save-dev @openzeppelin/truffle-upgrades
Migrating the CLI project
This is a one way process. Make sure you keep backups or version control copies of your .openzeppelin/ folder.
|
Now, let’s migrate our project by running:
$ npx migrate-oz-cli-project
✔ Successfully migrated .openzeppelin/rinkeby.json
✔ Migration data exported to openzeppelin-cli-export.json
✔ Deleting .openzeppelin/project.json
These were your project's compiler options:
{
"compilerSettings": {
"optimizer": {
"enabled": false,
"runs": "200"
}
},
"typechain": {
"enabled": false
},
"manager": "openzeppelin",
"solcVersion": "0.6.12",
"artifactsDir": "build/contracts",
"contractsDir": "contracts"
}
This script was installed along with the plugins and what it does is to delete the CLI project file and turn your old network files (all of which live under the .openzeppelin
directory) into their Upgrades plugin equivalent. Again, nothing on chain is changed, only local files. Notice also that once you’ve run this you can no longer use the CLI to manage this project’s contracts unless you revert the changes through backups or version control.
The migration script will also export a openzeppelin-cli-export.json
file into your working directory containing all the data that the CLI used to manage for you and now you’re free to use however you think it’s best. This is including your compiler settings, which are also printed at the end of the migration for convenience. Let’s add them to our new project config:
Copy compiler settings into the solidity
field in the Hardhat config file
// hardhat.config.js
// ...
module.exports = {
// ...
solidity: {
version: "0.6.12",
settings: {
optimizer: {
enabled: false,
runs: 200
}
}
}
}
Copy compiler settings into the compilers
field of our Truffle config file
// truffle-config.js
module.exports = {
// ...
compilers: {
solc: {
version: "0.6.12",
settings: {
optimizer: {
enabled: false,
runs: 200
}
}
}
}
}
The solidity compiler configuration format is different in truffle-config.js and hardhat.config.js files
|
And that’s it, you have successfully migrated your CLI project. Let’s now try your new setup upgrading one of your migrated contracts.
Upgrade to a new version
Let’s say we had a Box
contract in our CLI project, deployed to the Rinkeby network. Then if we open our export file, we’ll see something like this:
// openzeppelin-cli-export.json
{
"networks": {
"rinkeby": {
"proxies": {
"openzeppelin-upgrades-migration-example/Box": [
{
"address": "0x500D1d6A4c7D8Ae28240b47c8FCde034D827fD5e",
"version": "1.0.0",
"implementation": "0x038B86d9d8FAFdd0a02ebd1A476432877b0107C8",
"admin": "0x1A1FEe7EeD918BD762173e4dc5EfDB8a78C924A8",
"kind": "Upgradeable"
}
]
}
}
},
"compiler": {
// we'll ignore compiler settings for this
}
}
What we’re seeing in here is the JSON representation of our upgradeable contract in terms of addresses:
-
address
: the proxy address (the proxy contract contains your upgradeable contract state) -
implementation
: the implementation address (your upgradeable contract logic) -
admin
: the address of the proxy admin, which will probably belong to aProxyAdmin
contract unless you set up otherwise
And this is how it would look like if we decided to upgrade our Box
contract to a BoxV2
contract using the plugins and this export file:
These scripts are just examples of how to use the exported data. We make no suggestions on whether to keep that file as it is or how to handle its data. This is up to the user now.
With Hardhat, we would write a script (you can read more about Hardhat scripts here and about using the hardhat-upgrades
plugin here):
// scripts/upgradeBoxToV2.js
const { ethers, upgrades } = require("hardhat");
const OZ_SDK_EXPORT = require("../openzeppelin-cli-export.json");
async function main() {
const [ Box ] = OZ_SDK_EXPORT.networks.rinkeby.proxies["openzeppelin-upgrades-migration-example/Box"];
const BoxV2 = await ethers.getContractFactory("BoxV2");
await upgrades.upgradeProxy(Box.address, BoxV2);
}
main();
$ npx hardhat run scripts/upgradeBoxToV2.js --network rinkeby
With Truffle, we would write a migration (you can read more about Truffle migrations here and about using the truffle-upgrades
plugin here):
// migrations/2_upgrade_box_contract.js
const { upgradeProxy } = require('@openzeppelin/truffle-upgrades');
const OZ_SDK_EXPORT = require("../openzeppelin-cli-export.json");
const BoxV2 = artifacts.require('BoxV2');
module.exports = async function (deployer) {
const [ Box ] = OZ_SDK_EXPORT.networks.rinkeby.proxies["openzeppelin-upgrades-migration-example/Box"];
const instance = await upgradeProxy(Box.address, BoxV2, { deployer });
console.log("Upgraded", instance.address);
};
$ npx truffle migrate --network rinkeby
And that’s it! You have migrated your OpenZeppelin CLI project to Truffle or Hardhat and performed an upgrade using the plugins.