openzeppelin-foundry-upgrades
openzeppelin-foundry-upgrades copied to clipboard
Support use with implementations and proxies from OpenZeppelin Contracts 4.x
Users may want to migrate from Hardhat to Foundry, and they may have contracts and/or proxies already deployed with OpenZeppelin Contracts 4.x.
This would include:
- [ ] Add support for 4.x UUPS implementations
- [ ] Add support for upgrading existing 4.x proxies
Note that we will probably not support deploying new 4.x proxies, but this feature will cover support for upgrading existing ones.
Originally requested in https://forum.openzeppelin.com/t/first-look-at-the-upcoming-openzeppelin-foundry-upgrades-library/38347/2
Same situation Want to migrate from Hardhat to Foundry with deployed contracts using UUPS with OZ [email protected] Is it possible to migrate to Foundry and how to use proxies there ?
If you need to call a function on your new implementation during the upgrade (such as to call a reinitializer), you can just call upgradeProxy
as documented in the readme.
If you don't need to call a function on your new implementation during the upgrade, you can do the following workaround:
- Call prepareUpgrade to pass in the new version of your implementation contract, which will validate that it is upgrade safe and will deploy the new implementation. Store the returned address in a variable.
- Invoke the upgradeTo function at your proxy address by calling
upgradeTo(address)
on the proxy, passing in the address of the new implementation that was returned in step 1.
What is different from doing this ?
vm.startBroadcast(deployPrivateKey);
address implementation = address(new MyContract());
console2.log(implementation);
address proxy = address(new ERC1967Proxy(implementation, ""));
MyContract myContract = MyContract(proxy);
vm.stopBroadcast();
I believe both are using ERC1967Proxy
contract from OZ, but the call from Upgrades.sol
contract is more safe?
Also, here we are initializing a new proxy I believe ? How to use existing proxy with having to call prepareUpgrade which have as input a proxy address ?
- If the proxy was deployed with Hardhat, since Foundry and Hardhat have different artifacts I believe, is that an issue for the continious migration ?
prepareUpgrade
is similar to address(new MyContractV2());
but it also validates the new implementation for upgrade safety as a prerequisite before deploying it.
In order for the validations to work, in MyContractV2, you should add an annotation to specify the previous version that it upgrades from. See examples in https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core#define-reference-contracts
Foundry does not use existing Hardhat artifacts. There is no need to, because the new annotation is used for validations, and you should recompile your contracts with Foundry instead of trying to reuse Hardhat compilation artifacts.
Here is an example of what the upgrade script could look like:
Options memory opts;
address newImpl = Upgrades.prepareUpgrade("MyContractV2.sol", opts);
MyContract myContract = MyContract(myProxyAddress);
myContract.upgradeTo(newImpl);
Can OpenZeppelin/openzeppelin-foundry-upgrades
be installed with a specific version ?
My contracts are using [email protected] which is incoherent with Upgrades.sol imports using ERC1976Utils, that are not in the Proxy package of [email protected]
Can we upgrade contracts with Foundry without using the library openzeppelin-foundry-upgrades
then, that's the only solution I see for this case.
Right, this issue will need to be fixed in order for 4.x to be used with Upgrades.sol.
To upgrade without openzeppelin-foundry-upgrades
, it would be like:
address newImpl = address(new MyContract());
MyContract myContract = MyContract(myProxyAddress);
myContract.upgradeTo(newImpl);
We still recommend validating storage layout compatibility before upgrading. You can do that using the validate
command from the Upgrades CLI.