Upgradable Contracts

I am trying to create a sample upgradable proxy contract deployment and have the following.

Math.sol

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract Math is Initializable, UUPSUpgradeable, OwnableUpgradeable {

  function initialize() public initializer {
    __Ownable_init();
    __UUPSUpgradeable_init();
  }

  function doSum(uint a, uint b) public pure returns (uint sum) {
    return a + b;
  }

  function _authorizeUpgrade(address) internal override onlyOwner {}
}

hardhat.config.js

require('dotenv').config();
require("@nomiclabs/hardhat-waffle");
require('@openzeppelin/hardhat-upgrades');
require("@nomiclabs/hardhat-etherscan");

const { ROPSTEN_API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env;
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
  defaultNetwork: "hardhat",
  networks: {
    hardhat: {},
    ropsten: {
      url: ROPSTEN_API_URL,
      accounts: [`0x${PRIVATE_KEY}`],
    },
  },
  etherscan: {
    // Your API key for Etherscan
    // Obtain one at https://etherscan.io/
    apiKey: ETHERSCAN_API_KEY
  }
};

I run npx hardhat compile, which compiles everything fine as can be seen below

$ npx hardhat compile
Compiling 1 file with 0.8.4
Solidity compilation finished successfully
✨  Done in 1.89s.

deploy.js

// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");
const { ethers, upgrades } = hre;

async function main() {
  // Hardhat always runs the compile task when running scripts with its command
  // line interface.
  //
  // If this script is run directly using `node` you may want to call compile
  // manually to make sure everything is compiled
  // await hre.run('compile');

  // We get the contract to deploy
  const Math = await hre.ethers.getContractFactory("Math");
  const instance = await upgrades.deployProxy(Math, { kind: 'uups' });


  
  await instance.deployed();
  console.log("Math Proxy deployed to:", instance.address);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

When running the deploy script, my contract deploys completely fine but when I replace the deployproxy code to use the following


  const instance = await upgrades.upgradeProxy('0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512', Math, { kind: 'uups' })
  console.log("Math Contract Upgraded");

I get the following error

Contract at 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 doesn't look like an administered ERC 1967 proxy

I'm not sure what is going on. Please let me know if I am doing something wrong or need to contact folks over at hardhat

2 Likes

I don't think you need to put the address, let openzepelin upgrade the contract for you

What do I put for the first param in the upgradeProxy call?

Sorry.
Maybe you don’t put the proxy address but the logic address

I tried using both the proxy and implementation addresses. I have attached the local hardhat network deployment meta below

{
  "manifestVersion": "3.2",
  "proxies": [
    {
      "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
      "txHash": "0xb8ba3bd84a64d10ee6798851e1cbdb72e34c3b39527ff663d9b1ed8b1b27af3a",
      "kind": "uups"
    }
  ],
  "impls": {
    "0a88c0392e5b6ceb13176fb1e141774f9dc37898346f3c82c068000d697d80b2": {
      "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
      "txHash": "0xbb6874d37837956cb3c626ceaedd7b4cfb8c441c4c6dd762990a5f1692d115ba",
      "layout": {
        "storage": [
          {
            "contract": "Initializable",
            "label": "_initialized",
            "type": "t_bool",
            "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:39"
          },
          {
            "contract": "Initializable",
            "label": "_initializing",
            "type": "t_bool",
            "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:44"
          },
          {
            "contract": "ERC1967UpgradeUpgradeable",
            "label": "__gap",
            "type": "t_array(t_uint256)50_storage",
            "src": "@openzeppelin/contracts-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:211"
          },
          {
            "contract": "UUPSUpgradeable",
            "label": "__gap",
            "type": "t_array(t_uint256)50_storage",
            "src": "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol:107"
          },
          {
            "contract": "ContextUpgradeable",
            "label": "__gap",
            "type": "t_array(t_uint256)50_storage",
            "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36"
          },
          {
            "contract": "OwnableUpgradeable",
            "label": "_owner",
            "type": "t_address",
            "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22"
          },
          {
            "contract": "OwnableUpgradeable",
            "label": "__gap",
            "type": "t_array(t_uint256)49_storage",
            "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:87"
          }
        ],
        "types": {
          "t_address": {
            "label": "address"
          },
          "t_array(t_uint256)49_storage": {
            "label": "uint256[49]"
          },
          "t_uint256": {
            "label": "uint256"
          },
          "t_array(t_uint256)50_storage": {
            "label": "uint256[50]"
          },
          "t_bool": {
            "label": "bool"
          }
        }
      }
    }
  }
}

I just try this morning, a full working exemple with my contract

// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const { parseEther } = require("@ethersproject/units");
const hre = require("hardhat");
const { ethers, upgrades } = require("hardhat");
require("@nomiclabs/hardhat-web3");

var owner, dev2, dev3, dev4;

async function main() {
  await hre.run("compile");

  [owner, dev2, dev3, dev4] = await ethers.getSigners();
  // Deploy store
  const storage = await ethers.getContractFactory("Storage");
  const instanceStorage = await upgrades.deployProxy(storage);
  await instanceStorage.deployed();
  console.log("storage address", instanceStorage.address);

  // Deploy market
  const market = await ethers.getContractFactory("Marketplace");
  const instance = await upgrades.deployProxy(market, [
    instanceStorage.address,
  ]);
  await instance.deployed();
  console.log("market address", instance.address);

  // reattach proxy to contract market (without this hardhat can't call market method)
  var marketPlace = market.attach(instance.address);

  // deploy nft contract
  const NFT = await ethers.getContractFactory("PublicNft");
  const nft = await NFT.deploy();
  await nft.deployed();
  nft.setMarketAddress(instance.address);
  // mint nft and create auction
  await mint(nft, marketPlace);

  // fetch auction
  const fetch = await marketPlace.fetchPageAuctions(0, 10);
  console.log("fetch", fetch);

  // deploy market v2
  var v2 = await ethers.getContractFactory("Marketplace2");
  await upgrades.upgradeProxy(instance.address, v2, []);

  // test v2
  marketPlace = v2.attach(instance.address);
  const tx2 = await marketPlace.cancel(0);
  await tx2.wait();
  const fetch2 = await marketPlace.fetchPageAuctions(0, 10);
  console.log("fetch 2", fetch2);
}

async function mint(nft, market) {
  const today = new Date();
  const tomorrow = new Date();

  // Add 1 Day
  tomorrow.setDate(today.getDate() + 1);
  const deadLine = parseInt(tomorrow.getTime() / 1000);

  let amount = ethers.utils.parseEther("0.1");

  for (var i = 0; i < 10; i++) {
    const tx = await nft.mint(
      owner.address,
      "Qmet7fiKVndRxv9bUHbpJDCZu5ohd65BAVWzWsT5zrWxBy" + i,
    );
    await tx.wait();
    const token = i + 1;
    // await nft.approve(market.address, token);
    // await nft.getApproved(token);

    const txAuction = await market.createAuction(
      nft.address,
      token,
      "Auction" + i,
      0,
      amount,
      deadLine,
      false,
    );
    txAuction.wait();
  }
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

@youtpout I am facing the exact same issue with UUPS Proxy as mentioned by @Lakshay_Sharma

Hello,
I try yesterday with uups tuto from openzeplin.

Check .openzeppelin folder on your repos you have json file with proxy address

It has @youtpout

On which chain you deployed ?

Yesterday I have some problem with upgrade, I change rpc url to resolve my problem

@youtpout I am trying to get it on Avalanche fuji testnet. On testnet its giving me RPC timeout errors when trying to upgrade implementation. On Local as mentioned above giving contract is not an administered erc1967 proxy

Care the proxy address change between chain

Try another rpc for your avax testnet

yes on AVAX testnet, able to do it now, after rpc change. But nevertheless, the issue remains on local hardhat. And if I want to write test cases I am not able to do because of this

The problem with local node if you stop it you lost all your data store in it

A solution can be to restore avax testnet node on your hardhat and try on it

@youtpout you mean to fork it using a hardhat forking mechanism? I tried this, still the same issue though

Personnaly for my test I create new contract every time I change data, so you can test upgrade functionnality with different contract implementation on local node

Yes I am creating new implementation contract but the new implementation contract when trying to deploy in the following way is throwing the error as mentioned above

await upgrades.upgradeProxy("<Proxy-Address>", ContractV2)

How you get your address

After I call the deployProxy() I get the proxy address which I am using in the upgradeProxy()

1 Like

Hi,
I was facing the same problem when I was trying upgrade a contract deployed at a Ganache instance running locally.
In my case, I just add the "--network localhost" arguments and the message was gone:

npx hardhat run --network localhost scripts/upgradeMyToken.ts