Cannot Upgrade UpgradeableContract

I am trying to upgrade an already deployed contract. Unfortunately I receive an Ownable: caller is not the owner error. Checking the actual owner of the contract at bscscan.com shows me the address that I am using (so I am the owner) for the upgrade. Also calling functions (sending transactions) that are allowed only as admin works fine (through the proxy).

My Contract has Implemented the OwnableUpgradeable as well as the ContextUpgradeable and IERC20Upgradeable contract. My "initializer" function is also marked with the "initializer" modifier and was actually called since some properties were set.

Any thoughts / ideas appreciated!

:1234: Code to reproduce

This is the code I am running to upgrade my contract (removed some parts of a function).

	const factory = await ethers.getContractFactory('MyContract', {
		signer: deployer,
	})

	const contract = (await upgrades.upgradeProxy(
		contractAddress,
		factory
	)) as MyContract

:computer: Environment

Hardhat

What kind of proxy are you using (transparent or UUPS)? Can you ensure that you are checking the actual owner using the proxy address (on bscscan), not the implementation address?

If possible, can you share the code for your implementation contract (or at least the _authorizeUpgrade function if you are using UUPS)?

Hey! Thanks for your response.

I am pretty sure that I have checked the proxy address since bscscan showed me the upgrade functions (see screenshot below).

Side note: I've also tried to call here the "upgradeTo" function, but it results in an revert as well. New version of the implementation is deployed, but the last part of the "upgradeProxy()" function is missing / reverting.

I think that I am using a "transparent" proxy - at least I followed this example from the docs https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable (and I don't have the _authorizeUpgrade function implemented - I mean, it could be in one of the libraries I have imported)

Since you are using a transparent proxy, ensure that the account from which you are invoking the upgrade is actually the admin. For example, if you call admin(), does it return the address of the account that you are using to call the upgrade?

That's the weird part - when I try to call the "admin()" write function it also results in a revert (see screenshot)

image

Really running out of ideas - calling functions that are marked with the "onlyOwner" modifier of the implementation (through the proxy) works also fine.

Ok, I have found something very interesting. I checked the logs of the Create contract transaction and found an event called AdminChanged besides three other events. The log contains two parameters previousAdmin & newAdmin. previousAdmin equals the zero-address and newAdmin is an address that I did not send the transaction from.

Long story short: I can see the creation transaction of my wallet. But the AdminChanged event contains a completely different address. Things are getting more weird: This address is another contract of type ProxyAdmin that was created 30 days earlier then my contract creation. But, somehow I am the creator of this contract (just with a different wallet).

EDIT: This contract is another upgradeable contract I deployed a month earlier. Weird part now is, why this contract is the admin of my new deployed proxy.

Thank you for that information. From what you described, here is what is happening:

  • For transparent proxies, a proxy admin contract is deployed. The upgrades plugins will only deploy a single proxy admin contract for all of your transparent proxies on each network.
    That means if you deployed a transparent proxy a month earlier (where the plugins deployed a proxy admin), and then you deploy another transparent proxy now, the plugin will reuse that same proxy admin contract for your new proxy.
  • The AdminChanged event was emitted by the _changeAdmin function that was called in the constructor of the transparent proxy. In other words, that was simply setting the admin of the proxy to be your proxy admin contract.
  • In order to upgrade your proxy, you should invoke the upgrade() function on the proxy admin contract. The proxy admin's upgrade() function calls the proxy's upgradeTo().

For more details, see https://docs.openzeppelin.com/contracts/4.x/api/proxy#TransparentUpgradeableProxy and https://docs.openzeppelin.com/contracts/4.x/api/proxy#ProxyAdmin