Before making this post, I was unsure what a complete start-to-finish workflow with the OpenZeppelin CLI would like like.
Thanks to @spalladino—a good chunk of the text in this guide was copied directly from his replies to my questions—I was able to put together this workflow in hopes that it will help anyone else with the same questions. It covers the steps for each stage of the development process, as well as some info on some unused commands and where those fit in.
Setup, Development, and Testing
-
Create and initialize your project with
npx oz init projectName
-
Link the OpenZeppelin contracts that will be used as dependencies with
npx oz link @openzeppelin/contracts-ethereum-package
- This installs the above package and loads the addresses of the implementation contracts for pre-deployed contracts from
@openzeppelin/contracts-ethereum-package
. This means that, if you create an upgradeable ERC20, you’ll be reusing bytecode previously deployed by us, so you save time and gas. - Check out this article to learn more about implementation contracts and proxy instances if the concept is confusing.
- This installs the above package and loads the addresses of the implementation contracts for pre-deployed contracts from
At this point we can develop and test our contracts, and just generally play around with the CLI to get a feel for things.
Deployment
-
If you did any test deployments (e.g. perhaps you were testing the upgrade process) you may want to reset the project before continuing on to the “real” deployment. This can be done with the steps from this forum post, which are summarized below. Note: The CLI does a good job at keeping stuff from different networks compartmentalized. You should only need to nuke your current settings in this way if you want to deploy from scratch on the same network. If you are moving from development to testnet, or testnet to mainnet, you don’t need to worry about this.
- Delete the
build
directory - Delete the applicable
<network>.json
file(s) - Remove all contracts from
project.json
usingnpx oz remove contractName1 contractName2
- Delete the
-
Compile all contracts with desired Solidity version, e.g.
npx oz compile --solc-version 0.5.12
-
Deploy each contract with
npx oz create
. During these step be sure to also call the appropriate initialization function on each contract.- The
create
command also adds the contracts toproject.json
- The
-
Verify the deployed contracts on Etherscan/Etherchain with
npx oz verify
. -
To reduce the risk of malicious upgrades you may want to change the upgradeability admin for all contracts to be a multisig or a hardware wallet. We can do this with
npx oz set-admin
.
At this point deployment is completed, and our contract/app can be used on whichever network we deployed to.
Upgrade Contracts
Now our contracts have been live for some time and we want to upgrade one or more of them.
-
If you changed the admin to a hardware wallet or other address in step 5 above, you may now want to change it back to perform the upgrade with the CLI
- If all contracts are controlled by the same admin address, one way to do this is by connecting your hardware wallet via MyCrypto and sending the transaction from there. You can call the
transferOwnership
method of the contract given by theproxyAdmin.address
field of<network.json>
. - If they contracts have different upgradeability admin addresses, you send the transaction to the proxy contract directly. If you changed the admin of a single proxy to an Address
A
, thenA
is managing that proxy directly and not via the ProxyAdmin. In that case, you need to callchangeAdmin
on the proxy fromA
. - Alternatively, you can send the below transactions directly from your hardware wallet with a web3 provider that connects to it, such as this one or this one. (Disclaimer: Be especially careful here, as those providers have not been tested/audited by OpenZeppelin).
- If all contracts are controlled by the same admin address, one way to do this is by connecting your hardware wallet via MyCrypto and sending the transaction from there. You can call the
-
Use
npx oz upgrade
to upgrade a single contract, ornpx oz upgrade --all
to upgrade all contracts. -
If you’ve added any new initialization functions that need to be called after upgrading, call them now. Since you won’t be able to use the
initializer
modifier again, make sure these functions are protected from being called more than once. See this post for an example implementation. -
If necessary, repeat steps 4 and 5 in the above section (verify contracts and change upgradeability admin)
-
If we ever decide we no longer want a contract to be upgradable, use
npx oz set-admin
to change the upgradeability admin to the zero address.
Other Tips and Notes
In the CLI commands docs, you’ll see a few other commands not used or mentioned above that will be discussed here.
The session
command is pretty handy. It’s there to make it easy to run many operations over the same network. You can run oz session --network rinkeby
, and all subsequent operations will be executed on Rinkeby automatically, so you don’t need to manually choose the network on every command. This command can also be used to set the default address transactions are sent from.
You don’t need to worry about freeze
, publish
, and bump
unless you are creating an Ethereum package (ie a reusable contracts library) yourself.
The add
and push
commands are there mostly for historic reasons. In the first versions of the CLI, in order to create a contract, you had to first add
it to your project, then push
the implementation contract, and then create
the upgradeable instance. In more recent versions, just running create
takes care of everything for you (note that you still need to manually run the add/push/create flow if you are running in no-interactive
mode, though this is probably going to change as well in the next major release).