Contract owner is 0x0 after upgrade

Below are two mock contracts to reproduce the situation that I'm facing:

  1. MyContractV1 is deployed on the local chain
  2. MyContractV2 is upgraded using the OpenZeppelin Upgrades hardhat plugin
    Observed: when checking for the .owner() for MyContractV2, the address 0x0 is returned instead of the owner of the MyContractV1.
    Expected: the owner address should be the same as for MyContractV1.

Now, this is very weird, since everything works with the tests, and all of them pass. But the actual upgrade, then, fails, and I can't call any function due to ownership issues (in the real contract).

MyContractV1.sol

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

import "./access/OwnableAccessControlUpgradeable.sol";
import "./proxy/UUPSUpgradeable.sol";

contract MyContractV1 is OwnableAccessControlUpgradeable, UUPSUpgradeable {
  string public name;

  function setName(string memory _newName) public {
    name = _newName;
  }

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

MyContractV2.sol

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

import "./MyContractV1.sol";

contract MyContractV2 is MyContractV1 {
  string public version;

  function setVersion(string memory _newVersion) public {
    version = _newVersion;
  }

}

OwnableAccessControlUpgradeable.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./OwnableUpgradeable.sol";
import "./AccessControlUpgradeable.sol";

contract OwnableAccessControlUpgradeable is OwnableUpgradeable, AccessControlUpgradeable {

    bytes32 public constant AUTHORIZED_OPERATOR =

        keccak256("AUTHORIZED_OPERATOR");

    function __OwnableAccessControl_init() internal initializer {

        __Ownable_init();

        __AccessControl_init();

        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

    }

}

V1 Deployment Tests

const { ethers } = require("hardhat");
const { Contract } = require("ethers");
const { expect } = require("chai");

// Start test block
describe("V1 test", function () {
  let myContractV1;
  let MyContractV1;
  let deployer;

  beforeEach(async function () {
    accounts = await ethers.getSigners();
    deployer = accounts[0];

    console.log(deployer.address, "deployer addr");

    MyContractV1 = await ethers.getContractFactory("MyContractV1");
    myContractV1 = await upgrades.deployProxy(MyContractV1, {
      kind: "uups",
    });

    console.log("MyContractV1 address:", myContractV1.address);

    await myContractV1.setName("My Contract V1");
  });
  context("V1 test", async () => {
    it("deployer is the contract owner", async () => {
      const owner = await myContractV1.owner();
      expect(owner.toString()).to.equal(deployer.address.toString());
    });

    it("checks the name", async () => {
      const name = await myContractV1.getName();
      expect(name, toString()).to.equal("My Contract V1");
    });
  });
});

V2 Upgrade Tests

const { ethers, upgrades } = require("hardhat");

const { Contract } = require("ethers");

const { expect } = require("chai");

// Start test block

describe("V2 upgrade tests", function () {

  let myContractV1;

  let MyContractV1;

  let MyContractV2;

  let myContractV2;

  let deployer;

  beforeEach(async function () {

    accounts = await ethers.getSigners();

    deployer = accounts[0];

    MyContractV1 = await ethers.getContractFactory("MyContractV1");

    myContractV1 = await upgrades.deployProxy(MyContractV1, {

      kind: "uups",

    });

    MyContractV2 = await ethers.getContractFactory("MyContractV2");

    myContractV2 = await upgrades.upgradeProxy(myContractV1, MyContractV2);

    console.log(myContractV2.address, "v2 address");

    await myContractV2.setVersion("2");

  });

  context("V2 test", async () => {

    it("deployer is the contract owner", async () => {

      const owner = await myContractV2.owner();

      expect(owner.toString()).to.equal(deployer.address.toString());

    });

    it("expects the version of 2", async () => {

      const version = await myContractV2.version();

      expect(version.toString()).to.equal("2");

    });

  });

});

./scripts/deploy.js

const { ethers } = require("hardhat");

async function main() {

  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);

  const MyContractV1 = await ethers.getContractFactory("MyContractV1");

  const myContractV1 = await upgrades.deployProxy(MyContractV1, {

    kind: "uups",

  });

  console.log("MyContractV1 address:", myContractV1.address);

  await myContractV1.setName("My Contract V1");

}

main()

  .then(() => process.exit(0))

  .catch((error) => {

    console.error(error);

    process.exit(1);

  });

./scripts/prepare_upgrade.js

const { ethers } = require("hardhat");

async function main() {

  const [deployer] = await ethers.getSigners();

  const myContractV1Address = "0x64296acc5af4B9DAF59C04412b987DEe9EA167E0";

  const MyContractV2 = await ethers.getContractFactory("MyContractV2");

  console.log("Deploying contracts with the account:", deployer.address);

  console.log("Preparing V2 upgrade...");

  const myContractV2Address = await upgrades.prepareUpgrade(

    myContractV1Address,

    MyContractV2

  );

  console.log("V2 address: ", myContractV2Address);

  const myContractV2 = await MyContractV2.attach(myContractV2Address);

  await myContractV2.setVersion("2");
  // Checking for the same owner
  const owner = await myContractV2.owner(); // this logs 0x0

}

main()

  .then(() => process.exit(0))

  .catch((error) => {

    console.error(error);

    process.exit(1);

  });

:computer: Environment

  1. Using Hardhat, 6.4.15
  2. EthersJS, 6.4.15

Hi @ruham88. Your post was a long time ago but I just saw it, sorry.

I believe the problem is in this line:

  const myContractV2 = await MyContractV2.attach(myContractV2Address);

myContractV2Address is the addres of your implementation contract, which is what prepareUpgrade returns, and you should be using the address of the proxy contract.

1 Like