Error Upgrading smart contracts

Hello,
I’m trying to upgrade an old Upgradeable smart contract and getting transaction errors when calling openzeppelin upgrade

Console output:

➜  platform-contracts git:(test/contribute_with_gateway) ✗ npm run update                                       

> ethichub_platform@0.1.7 update /home/dr-manhattan/workspace/platform-contracts
> oz update

? Pick a network kovan
Browserslist: caniuse-lite is outdated. Please run the following command: `npm update`
Nothing to compile, all contracts are up to date.
✓ Contract EthicHubDepositManager deployed
All contracts have been deployed
? Which instances would you like to upgrade? Choose by address
? Pick an instance to upgrade EthicHubDepositManager at 0x9a261A14D0f50f3dd58E7f232Fea3b702CDD7ef1
? Call a function on the instance after upgrading it? Yes
? Select which function * initialize_v2()
✖ Upgrading instance at 0x9a261A14D0f50f3dd58E7f232Fea3b702CDD7ef1 and calling 'initialize_v2' with no arguments
✖ Upgrading instance at 0x9a261A14D0f50f3dd58E7f232Fea3b702CDD7ef1
Proxy ethichub_platform/EthicHubDepositManager at 0x9a261A14D0f50f3dd58E7f232Fea3b702CDD7ef1 failed to upgrade with error: Transaction has been reverted by the EVM:
{
  "blockHash": "0xf583c29978044b796a83873c1b3173990980065d2358df396e733002e49c6454",
  "blockNumber": 20618549,
  "contractAddress": null,
  "cumulativeGasUsed": 23789,
  "from": "0xfbcb86e80ff9c864ba37b9bbf2be21cc71abcdee",
  "gasUsed": 23789,
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "root": null,
  "status": false,
  "to": "0x3d902aafcef9cc7c58f881a7cb672519341750d8",
  "transactionHash": "0x2d008faa65a69bea81d6dc2bfb8aff0667edaa5bc9a612e76fe21415c8380452",
  "transactionIndex": 0,
  "events": {}
}

New contract code:

pragma solidity 0.5.13;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";

import "../interfaces/IContributionTarget.sol";
import "../storage/EthicHubStorageInterface.sol";

contract EthicHubDepositManager is Initializable, Ownable, GSNRecipient {

    uint8 public version;
    EthicHubStorageInterface public ethicHubStorage;
    IERC20 public stableCoin;

    function initialize(
        address _ethicHubStorage, address _stableCoin
    ) public initializer {
        require(address(_ethicHubStorage) != address(0), "Storage address cannot is zero address");
        require(address(_stableCoin) != address(0), "Stable Coin address cannot is zero address");

        Ownable.initialize(_msgSender());
        GSNRecipient.initialize();

        ethicHubStorage = EthicHubStorageInterface(_ethicHubStorage);
        version = 1;
        stableCoin = IERC20(_stableCoin);
    }

    function initialize_v2() public initializer {
        version = 2;
    }

    function acceptRelayedCall(
        address relay,
        address from,
        bytes calldata encodedFunction,
        uint256 transactionFee,
        uint256 gasPrice,
        uint256 gasLimit,
        uint256 nonce,
        bytes calldata approvalData,
        uint256 maxPossibleCharge
    ) external view returns (uint256, bytes memory) {
      return _approveRelayedCall();
    }

    function _preRelayedCall(bytes memory context) internal returns (bytes32) {
    }

    function _postRelayedCall(bytes memory context, bool, uint256 actualCharge, bytes32) internal {
    }

    function contribute(address target, address contributor, uint256 amount) public {
        require(contributor != address(0), "Contributor address is zero address");
        require(
            address(target) == ethicHubStorage.getAddress(keccak256(abi.encodePacked("contract.address", target))),
            "Not a valid lending contract address"
        );
        require(
            ethicHubStorage.getBool(keccak256(abi.encodePacked("user", "investor", contributor))) ||
            ethicHubStorage.getBool(keccak256(abi.encodePacked("user", "representative", contributor))),
            "Contributor is not registered lender or borrower"
        );
        address token_sender = _msgSender();
        if (ethicHubStorage.getBool(keccak256(abi.encodePacked("user", "relayer", _msgSender())))) {
            token_sender = contributor;
        }
        require(
            stableCoin.balanceOf(token_sender) >= amount &&
            stableCoin.allowance(token_sender, address(this)) >= amount,
            "No balance allowed to transfer or insufficient amount"
        );
        require(
            amount > 0, "Amount cannot be 0"
        );

        

        require(stableCoin.transferFrom(token_sender, address(target), amount), "transferFrom dai failed");
        IContributionTarget(target).deposit(contributor, amount);
    }

    function setRelayHubAddress(address relayAddress) public onlyOwner {
        _upgradeRelayHub(relayAddress);
    }
}

Package.json file:

{
  "name": "ethichub_platform",
  "version": "0.1.7",
  "description": "Smart contracts of the EthicHub platform",
  "scripts": {
    "test": "scripts/test.sh",
    "console": "truffle develop",
    "compile": "oz compile",
    "coverage": "scripts/coverage.sh",
    "update": "oz update"
  },
  "repository": {
    "type": "git",
    "url": "git+https://gitlab.com/EthicHub/platform-contracts.git"
  },
  "keywords": [
    "ethichub",
    "crowdlending",
    "smart contracts",
    "ethereum",
    "solidity",
    "openzeppelin"
  ],
  "author": "EthicHub Dev Team: Raúl Martínez, Quijion Jiang, Diego Pardilla",
  "license": "GPL-3.0",
  "bugs": {
    "url": "https://gitlab.com/EthicHub/platform-contracts/issues"
  },
  "homepage": "https://gitlab.com/EthicHub/platform-contracts#readme",
  "dependencies": {},
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/node": "^7.2.2",
    "@babel/polyfill": "^7.2.5",
    "@babel/preset-env": "^7.2.3",
    "@babel/register": "^7.0.0",
    "@openzeppelin/cli": "^2.6.0",
    "@openzeppelin/contracts-ethereum-package": "^2.4.0",
    "@openzeppelin/gsn-helpers": "^0.2.3",
    "@openzeppelin/gsn-provider": "^0.1.9",
    "@openzeppelin/test-helpers": "^0.5.4",
    "@openzeppelin/upgrades": "^2.6.0",
    "@truffle/hdwallet-provider": "^1.0.23",
    "chai": "^4.1.2",
    "chai-as-promised": "^7.1.1",
    "chai-bignumber": "^3.0.0",
    "chai-bn": "^0.2.0",
    "dotenv": "^5.0.1",
    "ganache-cli": "^6.7.0",
    "solidity-coverage": "^0.5.0",
    "temp": "^0.8.3",
    "truffle": "^5.1.7",
    "web3": "^1.2.4",
    "web3-utils": "^1.2.2"
  }
}

Our kovan.json file

{
  "contracts": {
    "EthicHubDepositManager": {
      "address": "0x8621cF39d4CA278BE9F4dfC4f35D197806BcE911",
      "constructorCode": "608060405261263f806100136000396000f3fe",
      "bodyBytecodeHash": "05bd97c265761cc21cd3d3540e59b5fe42210c147f10cbf51350147135165cad",
      "localBytecodeHash": "c5261de55b3d7df82ed5b2247c32311f80398e89804f5b15a8a0575598140077",
      "deployedBytecodeHash": "c5261de55b3d7df82ed5b2247c32311f80398e89804f5b15a8a0575598140077",
      "types": {
        "t_bool": {
          "id": "t_bool",
          "kind": "elementary",
          "label": "bool"
        },
        "t_uint256": {
          "id": "t_uint256",
          "kind": "elementary",
          "label": "uint256"
        },
        "t_array:50<t_uint256>": {
          "id": "t_array:50<t_uint256>",
          "valueType": "t_uint256",
          "length": "50",
          "kind": "array",
          "label": "uint256[50]"
        },
        "t_address": {
          "id": "t_address",
          "kind": "elementary",
          "label": "address"
        },
        "t_uint8": {
          "id": "t_uint8",
          "kind": "elementary",
          "label": "uint8"
        }
      },
      "storage": [
        {
          "contract": "Initializable",
          "path": "@openzeppelin/upgrades/contracts/Initializable.sol",
          "label": "initialized",
          "astId": 1451,
          "type": "t_bool",
          "src": "757:24:11"
        },
        {
          "contract": "Initializable",
          "path": "@openzeppelin/upgrades/contracts/Initializable.sol",
          "label": "initializing",
          "astId": 1453,
          "type": "t_bool",
          "src": "876:25:11"
        },
        {
          "contract": "Initializable",
          "path": "@openzeppelin/upgrades/contracts/Initializable.sol",
          "label": "______gap",
          "astId": 1509,
          "type": "t_array:50<t_uint256>",
          "src": "1951:29:11"
        },
        {
          "contract": "Ownable",
          "path": "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol",
          "label": "_owner",
          "astId": 1197,
          "type": "t_address",
          "src": "526:22:8"
        },
        {
          "contract": "Ownable",
          "path": "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol",
          "label": "______gap",
          "astId": 1310,
          "type": "t_array:50<t_uint256>",
          "src": "2471:29:8"
        },
        {
          "contract": "GSNRecipient",
          "path": "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol",
          "label": "_relayHub",
          "astId": 67,
          "type": "t_address",
          "src": "1133:25:1"
        },
        {
          "contract": "EthicHubDepositManager",
          "path": "contracts/deposit/EthicHubDepositManager.sol",
          "label": "version",
          "astId": 2122,
          "type": "t_uint8",
          "src": "514:20:16"
        },
        {
          "contract": "EthicHubDepositManager",
          "path": "contracts/deposit/EthicHubDepositManager.sol",
          "label": "ethicHubStorage",
          "astId": 2124,
          "type": "t_address",
          "src": "540:47:16"
        },
        {
          "contract": "EthicHubDepositManager",
          "path": "contracts/deposit/EthicHubDepositManager.sol",
          "label": "stableCoin",
          "astId": 2126,
          "type": "t_address",
          "src": "593:24:16"
        }
      ],
      "warnings": {
        "hasConstructor": false,
        "hasSelfDestruct": false,
        "hasDelegateCall": false,
        "hasInitialValuesInDeclarations": false,
        "uninitializedBaseContracts": [],
        "storageUncheckedVars": [],
        "storageDiff": []
      }
    }
  },
  "solidityLibs": {},
  "proxies": {
    "ethichub_platform/EthicHubDepositManager": [
      {
        "address": "0x9a261A14D0f50f3dd58E7f232Fea3b702CDD7ef1",
        "version": "0.1.7",
        "implementation": "0x41Db9FA4Bc683b61E20e223d05B1c11865464b27",
        "admin": "0x3D902AafCEf9cc7c58F881a7CB672519341750d8",
        "kind": "Upgradeable"
      }
    ]
  },
  "manifestVersion": "2.2",
  "version": "0.1.7",
  "dependencies": {
    "@openzeppelin/contracts-ethereum-package": {
      "package": "0xB6F8F11b166D526932ee04ffe4D25B810f619E34",
      "version": "2.4.0"
    }
  },
  "proxyAdmin": {
    "address": "0x3D902AafCEf9cc7c58F881a7CB672519341750d8"
  }
}

Note I’m trying to call a different initializer because I think that was the way in the old docs. I tried modifying the existing initializer (basically changing version=1 to version=2) with same result

Tried redeploying the whole thing and upgrade a contract, same thing.

Thanks in advance, any help is appreciated

2 Likes

Hi @ethicraul,

We can’t use the initializer modifier for initializing state in an upgrade, if it was already used for initializing state in the original version. I assume this why the upgrade is reverting.

Instead we need to create a custom guard to ensure that any upgrade function is only called once.
If you already have a version you could do a check something like the following:

    function upgradeToV2() public {
        require(_version < 2, "MyContract: Already upgraded to version 2");
        _version = 2;
    }

:warning: Upgrade initialization should be appropriately tested and audited.


The following simple example hasn’t been tested.

MyContract.sol

// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

contract MyContract is Initializable {
    uint256 private _version;

    function initialize() public initializer {
        _version = 1;
    }
}

MyContract.sol (upgraded)

// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

contract MyContract is Initializable {
    uint256 private _version;

    function initialize() public initializer {
        _version = 1;
    }

    function upgradeToV2() public {
        require(_version < 2, "MyContract: Already upgraded to version 2");
        _version = 2;
    }
}

Deploy

$ npx oz deploy
Nothing to compile, all contracts are up to date.
? Choose the kind of deployment upgradeable
? Pick a network development
? Pick a contract to deploy MyContract
✓ Added contract MyContract
✓ Contract MyContract deployed
All implementations have been deployed
? Call a function to initialize the instance after creating it? Yes
? Select which function * initialize()
✓ Setting everything up to create contract instances
✓ Instance created at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
To upgrade this instance run 'oz upgrade'
0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B

Upgrade

$ npx oz upgrade
? Pick a network development
? Which instances would you like to upgrade? Choose by address
? Pick an instance to upgrade MyContract at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
? Call a function on the instance after upgrading it? Yes
? Select which function upgradeToV2()
Nothing to compile, all contracts are up to date.
✓ Contract MyContract deployed
All implementations have been deployed
✓ Instance upgraded at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B. Transaction receipt: 0x1f8958e5541d32684013244835c4fbcfa61d68b395c650051e7e86b481087a35
✓ Instance at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B upgraded

As an aside, we released OpenZeppelin Upgrades Plugins for Buidler and Truffle so that we can upgrade in Buidler and Truffle.

1 Like

Hi @ethicraul,

I wanted to check if this resolved your issue?

Yes, it was both what you posted and also we were using wrong upgradeability admin to launch the migration.

1 Like