Is it possible to override methods in proxy contract (without ruining all the upgrade magic the cli does for me)?
I’m trying to make it such that upgradeTo
can only be called once.
Is it possible to override methods in proxy contract (without ruining all the upgrade magic the cli does for me)?
I’m trying to make it such that upgradeTo
can only be called once.
Hi @timigod,
Instead of overriding the Proxy contract:
To prevent an upgradeable contract from being upgraded, you could just call changeProxyAdmin
to set the newAdmin to 0x1 address (0x0 is prevented).
Does that work for your use case?
Good idea but it doesn’t exactly work for this use case.
I want it to be known that it’ll only be upgraded one time and there’s no better way to enforce that than with code. If I did things the way you suggested, users will have to trust the admin.
Hi @timigod,
As far as I know, overriding the proxy would then mean that you couldn’t use the CLI.
You could look at having any restrictions being part of the upgrades governance instead:
See the example of using a multi-sig for upgrades governance: https://docs.openzeppelin.com/sdk/2.5/upgrades-governance
Also instead of adding restrictions to the proxy contract, look at adding restrictions on the admin (once deployed).
I thought as much @abcoathup. Thanks.
I’m currently considering forking the CLI code & changing the Proxy (template) from there. So I’d be able to use my modified CLI to deploy & upgrade.
Do you have any immediate tips/thoughts/warnings?
Hi @timigod,
My suggestion would be to look at adding restrictions to an overriden ProxyAdmin contract.
This would give you the benefit of using the OpenZeppelin CLI for development and deployment. As soon as you have deployed you could change the admin of the proxy to a proxy admin that only allows one upgrade, prior to announcing that your contract is live to your users.
That way you get the functionality you want, the benefit of the CLI and without having to fork the CLI.
Ah @abcoathup , I just looked into this and it makes perfect sense. I didn’t initially realize that upgradeTo
& upgradeToAndCall
were external
and as such can only be called by another contract (in this case, only the ProxyAdmin
contract).
Thanks!
@abcoathup I’m having issues with this. These are the steps I followed:
--minimal
) my overridden ProxyAdmin
contract (see code below). Called initialize(address _owner, uint 256 _allowedUpgrades)
and set the owner to the default (0 index) account in my local ganache setup.set-admin
to change the proxy admin to the address of the contract deployed in step 1. I also made sure to double check that the transaction is being sent from the same default (0 index) account.upgrade
command.I keep getting this error message “No contract instances that match contract ContractName are owned by this project”
I assumed that by sending the upgrade transaction from the same address as the owner of the ProxyAdmin
contract, it’ll all work. Apparently not. How do I make it such that the project still owns the contract?
Here’s my custom ProxyAdmin
code:
import "@openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol";
import "@openzeppelin/upgrades/contracts/ownership/Ownable.sol";
contract ProxyAdmin is OpenZeppelinUpgradesOwnable {
uint256 public allowedUpgrades = 0;
uint256 public upgradeCount = 0;
bool internal initialized;
function initialize(address _owner, uint256 _allowedUpgrades) public {
require(!initialized);
_transferOwnership(_owner);
allowedUpgrades = _allowedUpgrades;
initialized = true;
}
function getProxyImplementation(AdminUpgradeabilityProxy proxy) public view returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("implementation()")) == 0x5c60da1b
(bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
require(success);
return abi.decode(returndata, (address));
}
function getProxyAdmin(AdminUpgradeabilityProxy proxy) public view returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("admin()")) == 0xf851a440
(bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
require(success);
return abi.decode(returndata, (address));
}
function upgrade(AdminUpgradeabilityProxy proxy, address implementation) public onlyOwner {
require(upgradeCount < allowedUpgrades);
proxy.upgradeTo(implementation);
upgradeCount++;
}
function upgradeAndCall(AdminUpgradeabilityProxy proxy, address implementation, bytes memory data) payable public onlyOwner {
require(upgradeCount < allowedUpgrades);
proxy.upgradeToAndCall.value(msg.value)(implementation, data);
upgradeCount++;
}
}
Hi @timigod,
I repeated steps 1-4 and got the same result (as I would expect).
In my <network>.json I changed the address of the proxyAdmin
to the address of MyProxyAdmin
contract and repeated step 4 and was able to upgrade via the CLI using upgrade
.
I assume you should call your ProxyAdmin contract by a different name e.g. something like LimitedUpgradesProxyAdmin
.
Could you inherit from ProxyAdmin
instead of recreating?
The current implementation is a count of upgrades for proxies that the ProxyAdmin
is the admin for. The upgrade count may need to be per proxy depending on your requirements.
You need to check whether set-admin
could be used to change to an unrestricted ProxyAdmin
?
Please note that minimal proxy support is still experimental as per the warning:
Minimal proxy support is still experimental.
In upgradeable contracts we can’t set initial values in a field declaration (see the warning below) e.g. uint256 public upgradeCount = 0;
- Contract MyProxyAdmin or one of its ancestors sets an initial value in a field declaration. Consider moving all field initializations to an initializer function. See https://docs.openzeppelin.com/sdk/2.5/writing_contracts.html#avoid-initial-values-in-fields-declarations.
See the documentation for details: https://docs.openzeppelin.com/sdk/2.5/writing-contracts.html#avoid-initial-values-in-fields-declarations
If you create your own ProxyAdmin then this would need appropriate testing and auditing to ensure operation as expected.
My thoughts would still be that rather than create your own ProxyAdmin, it would be to do the restriction via governance e.g. a multi-sig, or explain to your users that once the project was at a certain stage then upgradeability would be removed.
Thanks a lot @abcoathup, I tried that and it worked. As for the other things you said:
ProxyAdmin
would certainly look neater. But I want to remove the ability to change the admin again (like I did above)Hi @timigod,
Good to hear that you are planning an audit.
I recommend reading the following checklist for before an audit:
I found reading past audits very useful: https://blog.openzeppelin.com/security-audits/
For details on OpenZeppelin audits, please see the website: https://openzeppelin.com/security-audits/