upgrades.deployProxy did *not* deploy proxy (confused, fun puzzle?)

Hello,

I have recently deployed a contract to the Ethereum Mainnet that I intended to be upgradeable using OpenZeppelin packages.

The deploy went flawlessly, users were interacting with my contract, and everyone was having fun in the telegram playing with it.

However, when I went to upgrade my contract, I noticed something very odd and disheartening:
There was no proxy address registered in my .openzeppelin > mainnet.json file.

I reviewed my test network deployments, they all had proxy contracts registered for each test deployment, but not the mainnet.

I'm very confused on what could have caused this. Read on to review my functions and config settings.

I'm very much looking forward to your responses and puzzling on how this scenario came about - I was very surprised to learn I could not update my contract!

:1234: Code to reproduce

Here is the deploy script (Variables Changed)

async function main () {
    const ExampleToken = await ethers.getContractFactory('ExampleContract');
    console.log('Deploying ExampleToken...');
    const exmpl = await upgrades.deployProxy(ExampleToken);
    await exmpl.deployed();
    console.log('exmpl deployed to:', exmpl.address);
  }
  
  main();

And then here is my hardhat.config.js

module.exports = {
  solidity: "0.8.4",
  optimizer: {
    enabled: true,
    runs: 1000,
  },
  networks: {
    mainnet: {
      url: `${url}`,
      accounts: [`${privateKey}`],
      live: true,
      saveDeployments: true,
      tags: ["production"],
      gasPrice: 69000000000,
    },
    gnosis: {
      url: `https://rpc.gnosischain.com/`,
      accounts: [`${privateKey}`],
      live: true,
      saveDeployments: true,
      tags: ["staging"],
    }
  },
  etherscan: {
    // Your API key for Etherscan
    // Obtain one at https://etherscan.io/
    apiKey: etherScan
  },
  gasReporter: {
    currency: "usd",
    token: "eth",
    gasPrice: 37
  }
};

:computer: Environment

Here are my project dependencies:

  "dependencies": {
    "@nomiclabs/hardhat-etherscan": "^3.0.3",
    "@nomiclabs/hardhat-waffle": "^2.0.3",
    "@openzeppelin/contracts-upgradeable": "^4.5.2",
    "@openzeppelin/hardhat-upgrades": "^1.17.0",
    "@poanet/solidity-flattener": "^3.0.7",
    "chai": "^4.3.6",
    "dotenv": "^16.0.0",
    "ethers": "^5.6.4",
    "hardhat": "^2.9.3",
    "hardhat-contract-sizer": "^2.5.1",
    "hardhat-gas-reporter": "^1.0.8",
    "solidity-coverage": "^0.7.20"
  }

And finally, the deployment EOA implicated in the accounts: [${privatekeys}] declaration is a Metamask wallet that I exported the private keys from to store in dotenv.

Hi @hanvalen,

Did you commit the .openzeppelin/mainnet.json to source control? It is recommended to commit that so you don't lose the information for your deployments.

However, there is a way to recover. You can just use the forceImport command to import your previously deployed proxy by passing in its mainnet address and the ethers contract factory of your existing (v1) implementation.

After it is imported, you will be able to upgrade that proxy.

1 Like

Continuing the discussion from upgrades.deployProxy did not deploy proxy (confused, fun puzzle?):

Hi @ericglau,

Thanks for your help.
The file is committed to source control.

I tried the forceImport command, and received this error:

C:\Users\[my-username]\dev\[project-folder]\node_modules\@openzeppelin\hardhat-upgrades\src\force-import.ts:54
      throw new ForceImportUnsupportedError(proxyOrBeaconAddress);
            ^
Error: Contract at address [contractAddress] doesn't look like a supported proxy or beacon

Only transparent, UUPS, or beacon proxies or beacons can be used with the forceImport() function.

For more detail,

The .openzeppelin > mainnet.json file looks like:

{
  "manifestVersion": "3.2",
  "proxies": [],
  "impls": {
      "hashOfSomthing": {
          "address": [deployedContractAddress],
          "txHash": [deploymentHash],
          "layout": {[layoutdetails]}
       }
   }
}

where the .openzeppelin > unknown-100.json (gnosis chain mainnet deployment for testing) looks like:

{
  "manifestVersion": "3.2",
  "admin": {
      "address": [an address],
      "txHash": [a hash],
   }
  "proxies": [
   {
    "address": [an address],
    "txHash": [a tx hash],
    "kind": "transparent"
   }, etc...
],
  "impls": {
      "hashOfSomthing": {
          "address": [deployedContractAddress],
          "txHash": [deploymentHash],
          "layout": {[layoutdetails]}
       }
   }
}

@hanvalen Can you ensure that you are passing the proxy's address (not the implementation address) to the forceImport command?

Hi @ericglau ,

The proxy address is what I'm looking for. It didn't get written down in the .openzeppelin > mainnet.json file after deployment

The address that users are interacting with should be your proxy contract. You can also look at the transaction history of the account that deployed the implementation contract, and find the transaction that deployed the proxy (which should have happened after the implementation contract's deployment).

Hi @ericlau,

Thanks so much for your help today.

That didn't happen - the tx just launched one contract, even though I used the upgrades.deployProxy function in my deploy script, as noted above. I'm wondering what would have caused that, is all.

@hanvalen Have your users been using the implementation contract directly on mainnet?

Were there any errors or messages when you originally ran upgrades.deployProxy for mainnet? Was your script exactly the same as for testnets? Would you be able to share your deployed address so that I can take a look (can DM me if you prefer).

It did error, it did inform me that it timed out and to rerun the function... but I think rerunning the function errored because there wasn't enough funds in the wallet.

I think I deduced what happened based on remembering this:
There was not enough ether in the wallet to deploy the proxy admin after deploying the implementation.
I could not later deploy the proxy admin as client began interacting with the contract.

It would be nice to have some kind of gas estimator that covers both implementation + proxyAdmin deploys so other people aren't surprised by this

Hi @hanvalen, thank you for those details. I've opened issue https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/562 to track your suggestion.

Since users have been using the implementation directly, and since that is not a proxy, unfortunately that cannot be upgraded and you would need a way to migrate the data to an actual proxy. I apologize since I originally thought you meant the proxy was simply missing from mainnet.json.

In general, to avoid this problem of users interacting directly with the implementation, one approach can be to initialize the implementation address itself with nonsensical values, or with some combination of values and logic that makes it unusable.

Continuing the discussion from upgrades.deployProxy did not deploy proxy (confused, fun puzzle?):

Experiencing something similar after I have deployed an upgrade using my proxy contract. I have testing my contracts in Mumbai, and last time I upgraded my contract was few weeks ago. Today after I used the same script to upgrade my contract I experienced something unusual.

My proxy contract: 0x87dB0372360f4b99c89d777E935D7c5424b431a1
Previous implementation: 0x427cC0Bf91A06E5Ce705EE563864E311DC56594a
Current implementation: 0x19690fE318936948Ec17Aa91BBBd5E235FABFB98

I have verified both implementations using hardhat's verify function.

There are 2 unusual things:

  1. I used to interact with my proxy contract using the "Read as proxy" and "Write as proxy" function in Polygonscan. However, after I have deployed my upgrade today, the "Write as proxy" page no longer works, it said my implementation is not verified ("Read as proxy" is working as usual)
  2. I tried to deploy another upgrade again, I got the following error. (forceImport function does not work either)

First error when I tried to upgrade
Error: Deployment at address 0x19690fE318936948Ec17Aa91BBBd5E235FABFB98 is not registered
To register a previously deployed proxy for upgrading, use the forceImport function.

Second error when I tried to force import
0x87dB0372360f4b99c89d777E935D7c5424b431a1 --contract-name EAWCMultipleTokenV3
Error: Contract at address 0x87dB0372360f4b99c89d777E935D7c5424b431a1 doesn't look like a supported proxy or beacon

Only transparent, UUPS, or beacon proxies or beacons can be used with the forceImport() function.

@Max_Lau Did you get any errors when you originally deployed the proxy or when you first upgraded it to 0x19690fE318936948Ec17Aa91BBBd5E235FABFB98? Do you still have your network file for Mumbai (.openzeppelin/unknown-80001.json) after you upgraded to 0x19690fE318936948Ec17Aa91BBBd5E235FABFB98?

Your proxy looks like it has "Write as proxy" on Polygonscan now -- maybe it was a glitch on Polygonscan.

For forceImport, make sure you are using the correct network (e.g. specify the --network <network name> parameter when you run the Hardhat script). I tried importing your proxy like the following and it worked:

  const Contract = await hre.ethers.getContractFactory("EAWCMultipleTokenV3");
  const deployment = await upgrades.forceImport('0x87dB0372360f4b99c89d777E935D7c5424b431a1', Contract);
  console.log("Proxy imported from:", deployment.address);