openzeppelin-foundry-upgrades icon indicating copy to clipboard operation
openzeppelin-foundry-upgrades copied to clipboard

Support use with implementations and proxies from OpenZeppelin Contracts 4.x

Open ericglau opened this issue 1 year ago • 6 comments

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

ericglau avatar Dec 06 '23 18:12 ericglau

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 ?

mrhouzlane avatar Jan 11 '24 12:01 mrhouzlane

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:

  1. 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.
  2. 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.

ericglau avatar Jan 11 '24 13:01 ericglau

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 ?

mrhouzlane avatar Jan 11 '24 13:01 mrhouzlane

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);

ericglau avatar Jan 11 '24 16:01 ericglau

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.

mrhouzlane avatar Jan 12 '24 10:01 mrhouzlane

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.

ericglau avatar Jan 12 '24 17:01 ericglau