OpenZeppin Upgrades Plugin - Testing with Hardhat

Hello Everyone!

I am working with Upgrade Plugin to implement a basic upgradability feature from V1 to V2. But I am not seeing the expected results in the Hardhat tests.

This is my implementation V1 contract;

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

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

contract ImplementationV1 is Initializable {
    uint256 private _value;

    // * Emitted when the stored value changes
    event ValueChanged(uint256 value);

    function initialize() public initializer {
        _value = 0;
    }

    // * Stores a new value in the contract
    function store(uint256 value) public {
        _value = value;
        emit ValueChanged(value);
    }

    // * Reads the last stored value
    function retrieve() public view returns (uint256) {
        return _value;
    }

    // * get the implementation version.
    function version() public pure returns (uint256) {
        return 1;
    }
}

And this is my Implementation V2 contract;

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

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

contract ImplementationV2 is Initializable {
    uint256 private _value;

    // * Emitted when the stored value changes
    event ValueChanged(uint256 value);

    function initialize() public initializer {
        _value = 0;
    }

    // * Stores a new value in the contract
    function store(uint256 value) public {
        _value = value;
        emit ValueChanged(value);
    }

    // * Reads the last stored value
    function retrieve() public view returns (uint256) {
        return _value;
    }

    // * get the implementation version.
    function version() public pure returns (uint256) {
        return 2;
    }

    // Increments the stored value by 1
    function increment() public {
        _value = _value + 1;
        emit ValueChanged(_value);
    }
}

And this is my test file;

import { ethers, network, upgrades } from "hardhat";
import { developmentChains } from "../helper-hardhat-config";
import { Contract, ContractFactory, ContractTransaction } from "ethers";
import { expect } from "chai";

!developmentChains.includes(network.name)
    ? describe.skip
    : describe("Proxy Unit Tests", function () {
          let implementationV1: ContractFactory;
          let implementationV2: ContractFactory;
          let proxy: Contract;
          let upgradedProxy: Contract;
          let version: number;

          beforeEach(async () => {
              implementationV1 = await ethers.getContractFactory(
                  "ImplementationV1"
              );

              proxy = await upgrades.deployProxy(implementationV1);
          });

          it("should read the value and version no from implementation V1.", async () => {
              const initialValue = await proxy.retrieve();
              version = await proxy.version();
              expect(initialValue).to.be.equal(0);
              expect(version).to.be.equal(1);
          });

          it("should store the value from implementation V1.", async () => {
              const tx: ContractTransaction = await proxy.store(40);
              await tx.wait(1);

              const value = await proxy.retrieve();
              expect(value).to.be.equal(40);
          });

          describe("After Updating to Implementation V2.", () => {
              beforeEach(async () => {
                  implementationV2 = await ethers.getContractFactory(
                      "ImplementationV2"
                  );

                  upgradedProxy = await upgrades.upgradeProxy(
                      proxy.address,
                      implementationV2
                  );
              });

              it("should read the value and version no from implementation V2.", async () => {
                  version = await upgradedProxy.version();
                  expect(version).to.be.equal(2);

                  //   const value = await upgradedProxy.retrieve();
                  //   expect(value).to.be.equal(40);
              });

              it("should increment the value from implementation V2.", async () => {
                  const tx: ContractTransaction =
                      await upgradedProxy.increment();
                  await tx.wait(1);

                  const value = await upgradedProxy.retrieve();
                  expect(value).to.be.equal(41);
              });
          });
      });

Here are the last two tests that do not give the expected results. If I have updated the value to 40 using proxy implementation V1 contract then if I increment it using implementation V2 contract then the results should be 41 but instead, it gives 1.

Here is the working repository link in case you want to test it locally LINK.

Thanks

It looks like you are deploying a new proxy in beforeEach so that proxy is not kept between test cases.

@ericglau Thank you so much, I did a mistake beforeEach should look like this for the last two tests;

beforeEach(async () => {
    const tx: ContractTransaction = await proxy.store(40);
    await tx.wait(1);

    implementationV2 = await ethers.getContractFactory(
        "ImplementationV2"
    );

    upgradedProxy = await upgrades.upgradeProxy(
        proxy.address,
        implementationV2
    );
});