Proxy admin is not the one registered in the network manifest

Following the tutorial here, I’ve created MyToken.sol (see here), which is reproduced below:

// contracts/MyToken.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol";

contract MyToken is Initializable, ERC20UpgradeSafe, AccessControlUpgradeSafe {
    
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    function initialize(address admin, uint256 initialSupply) public initializer {
        __ERC20_init("MyToken", "MYT");
        __AccessControl_init_unchained();
        _mint(admin, initialSupply * (10 ** uint256(decimals())));
        _setupRole(BURNER_ROLE, admin);
        _setupRole(DEFAULT_ADMIN_ROLE, admin);
    }

    function burn(address from, uint256 amount) public {
       require(hasRole(BURNER_ROLE, msg.sender), "Caller is not a burner");
       _burn(from, amount);
   }

   function revokeRole(bytes32 role, address account) public override {
       require(
           role != DEFAULT_ADMIN_ROLE,
           "ModifiedAccessControl: cannot revoke default admin role"
       );
 
       super.revokeRole(role, account);
   }

}

and deployed as below:

// 1_deploy_mytoken.js
const MyToken = artifacts.require('MyToken');
 
const { deployProxy } = require('@openzeppelin/truffle-upgrades');
 
module.exports = async function (deployer, network, accounts) {
  await deployProxy(MyToken, ['0x63e4C1725fEB9EDc83d7951cFeD6e0fC3132A26f', 1000], { deployer, unsafeAllowCustomTypes: true, initializer: 'initialize' });
};

Deployed on testnet using below:

npx truffle migrate --network rinkeby --skipDryRun

And the contract is successfully deployed (MyToken, ProxyAdmin, and AdminUpgradeabilityProxy). Checked in truffle console as well and works good.

Now I am adding a new function mint to MyTokenV2.sol in the last (after revokeRule function) as below:

// contracts/MyToken.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol";

contract MyTokenV2 is Initializable, ERC20UpgradeSafe, AccessControlUpgradeSafe {
    ...everything_as_per_MyToken...

   function mint(address to, uint256 amount) public virtual {
        require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "MyTokenV2: must have admin role to mint");
        _mint(to, amount);
   }
}

and migrating using:

// 2_upgrade_mytoken.js
const MyToken = artifacts.require('MyToken');
const MyTokenV2 = artifacts.require('MyTokenV2');
 
const { upgradeProxy } = require('@openzeppelin/truffle-upgrades');
 
module.exports = async function (deployer, network, accounts) {
  const myToken = await MyToken.deployed();
  await upgradeProxy(myToken.address, MyTokenV2, {from: '0x63e4C1725fEB9EDc83d7951cFeD6e0fC3132A26f ', deployer, unsafeAllowCustomTypes: true });
};

Now deploying using:

npx truffle migrate -f 2 --network rinkeby

am getting the error:

2_upgrade_mytoken.js
========================

Error: Proxy admin is not the one registered in the network manifest
    at upgradeProxy (/Users/name/projects/project-name/node_modules/@openzeppelin/truffle-upgrades/src/upgrade-proxy.ts:76:11)
    at module.exports (/Users/name/projects/project-name/migrations/2_upgrade_mytoken.js:9:3)
    at Migration._deploy (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/migrate/Migration.js:73:1)
    at Migration._load (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/migrate/Migration.js:55:1)
    at Migration.run (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/migrate/Migration.js:171:1)
    at Object.runMigrations (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/migrate/index.js:150:1)
    at Object.runFrom (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/migrate/index.js:110:1)
    at runMigrations (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/core/lib/commands/migrate.js:264:1)
    at setupDryRunEnvironmentThenRunMigrations (/Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/core/lib/commands/migrate.js:257:1)
    at /Users/name/projects/project-name/node_modules/truffle/build/webpack:/packages/core/lib/commands/migrate.js:220:1
Truffle v5.1.50 (core: 5.1.50)
Node v14.13.1

In .openzeppelin/rinkeby.json, I have:

{
  "manifestVersion": "3.1",
  "impls": {
    "06533079dc4a3f40961f3e7e72470a3e48b1944dcc9a0679070354dde8f659a8": {
      "address": "0x3Cc0513C73fC340785e120F18Bc2D69d7c8c32B3",
      "txHash": "0xaa3e5e884404110a181cc9480fa714dec8ea2a924a6b4dce453f3d12b19295f94",
      "layout": {...
      }
  },
  "admin": {
    "address": "0xbcC3803B405c8854dDaE429828459b238f9B8402",
    "txHash": "0xf582c24235538ef27a26b0618ccd01998943f328b8a826bde0e285f72cddf54f"
  }
}

What I’m missing in 2_upgrade_mytoken.js? How to fix this? Thanks for the help!

1 Like

Hi @megatower66,

You don’t need the from argument in 2_upgrade_mytoken.js.
The owner of your ProxyAdmin contract will be the original account used to deploy (as I can’t see where you have changed this).

await upgradeProxy(myToken.address, MyTokenV2, {from: '0x63e4C1725fEB9EDc83d7951cFeD6e0fC3132A26f ', deployer, unsafeAllowCustomTypes: true });
};

I couldn’t find your ProxyAdmin you listed on Rinkeby:

The command line argument is --skip-dry-run, see: https://www.trufflesuite.com/docs/truffle/reference/truffle-commands#migrate

Using --skipDryRun doesn’t do anything on the command line.

$ npx truffle migrate --network rinkeby --skipDryRun
> Warning: possible unsupported (undocumented in help) command line option: --skipDryRun

I suggest skipping dry run in your truffle-config.js

    rinkeby: {
      provider: () => new HDWalletProvider(
        mnemonic, `https://eth-rinkeby.alchemyapi.io/v2/${alchemyApiKey}`
      ),
      network_id: 4,
      gasPrice: 10e9,
      skipDryRun: true
    }

I recommend adding your public network files to version control: https://docs.openzeppelin.com/upgrades-plugins/1.x/network-files#configuration-files-in-version-control


I get the error when I try to do the upgrade with the dry-run simulation as it can’t find the ProxyAdmin. Notice that the Migrations show dry-run.

$ npx truffle migrate -f 2 --network rinkeby

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Migrations dry-run (simulation)
===============================
> Network name:    'rinkeby-fork'
> Network id:      4
> Block gas limit: 10000000 (0x989680)


2_upgrade_mytoken.js
====================

Error: Proxy admin is not the one registered in the network manifest

I was able to upgrade using:

npx truffle migrate -f 2 --network rinkeby --skip-dry-run

I recommend double checking that your public network files contain the correct addresses. I suspect that the dry run has overwritten the values.

I assume that this is only on Rinkeby and you can skip the dry run on mainnet.

Hi @megatower66,

I just wanted to check how you got on with this?

Hi @abcoathup, I wasn't able to resolve the issue. Had to resort to resetting the contract during migration.

However, am still not clear about below:

You mean to say that we should always use skipDryRun while deploying on mainnet as dry-run could potentially overwrite public network files? If yes, then the whole point of using dry-run is defeated, right?

What's the correct sequence of commands to be used in case of deploying upgradable smart contracts? Is below correct?

  1. test on local
npx truffle migrate --network development
  1. then test on public test network, below will create .openzeppelin/ropsten.json
npx truffle migrate --network ropsten
  1. then if everything works fine (after interacting with the contract on public test net either through truffle console or in browser using web3.js framework, deploy on mainnet (below will generate .openzeppelin/mainnet.json
npx truffle migrate --network mainnet

Will this sequence be OK? And now to upgrade smart contract:

a) create MyTokenV2.sol
b) write migration script using upgradeProxy (and no need to use {from: 'some_address'} (as mentioned earlier)
c) repeat steps 1 to 3 but use -f 2 parameter (assuming new migration is renamed 2_filename.js e.g.,

 npx truffle migrate -f 2 --network local/ropsten/mainnet

and everything should work out?

1 Like

Hi @megatower66,

I just tried with the latest version of OpenZeppelin Upgrades Plugins for Truffle and was able to use the dry run functionality of Truffle, but only when I did a dry run for the deploy and the upgrade.

When I did an upgrade with a dry run when I hadn't done a dry run on the deploy it failed. I have created an issue for this: https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/241

I would add a step before.

  1. Run unit tests on implementation contracts (v1 and v2 etc).
    Run higher level tests on the upgradeable contracts (interact via the proxy).
    See OpenZeppelin Upgrades: Step by Step Tutorial for Truffle for an example.

Truffle migrations will handle this for you, if you use a unique number in order, such as:

1_initial_migration.js
2_deploy.js
3_upgrade.js

I would recommend after deployment to look at using a multi-sig to manage the upgrades.
See: https://docs.openzeppelin.com/learn/preparing-for-mainnet#set-admin

Hi @megatower66,

I just wanted to check if you had any more questions?

Hi @abcoathup, thanks for your reply!

Step 0 is a nice touch, it's so basic and my fault I didn't write it down earlier!

I actually wanted to use below in the migration script (post deployment) as mentioned here

await admin.transferProxyAdminOwnership(gnosisSafe);

But post migration of admin to a Gnosis safe address, how would one use upgradeProxy directly in the migration scrip? I guess that's not possible and one would have to rather use prepareUpgrade and then use OpenZeppelin app in the Gnosis web app? I'm referring to the full steps mentioned in OpenZeppelin Upgrades: Step by Step Tutorial for Truffle

I'd much rather change the admin back to my metamask wallet (how to do that using Gnosis?) , do the upgrade using upgradeProxy directly in migration in order not to copy paste address of the proxy (box.address, address of AdminUpgradeabilityProxy) and the address of the new implementation (boxV2.address, again address of new AdminUpgradeabilityProxy) in Gnosis safe web app.

But even if I was willing to manually copy-paste in the interest of security, how would I able to call another contract function from my Gnosis safe address? Is it possible?

1 Like

We can't use upgradeProxy directly in the migration script as the admin (and hence control of the upgrade) has been transferred to the control of the Gnosis Safe.

When using Gnosis Safe, we need to do prepareUpgrade and then either use the OpenZeppelin app in Gnosis Safe or use OpenZeppelin Defender.

I appreciate the dislike of copying and pasting addresses. Though transferring control of admin back to an EOA (Externally Owned Account) to do an upgrade and then back again sounds much worse.

If you haven't already, I suggest trying it out using Gnosis Safe, and also OpenZeppelin Defender.

The OpenZeppelin app in Gnosis Safe creates a proposal that is then executed (a transaction is sent to upgrade the proxy from the Gnosis Safe, which is the owner of the ProxyAdmin).

1 Like

Hi @megatower66,

We have found an error in Upgrades Plugins for Truffle and Hardhat. Users of the flag unsafeAllowCustomTypes should update their dependencies to the latest version.

See post for more details: Problem in Upgrades Plugins for users of unsafeAllowCustomTypes.

1 Like