deployProxy does not deploy a ProxyAdmin

Hey @ericglau , I am trying to implement TransparentUpgradeableProxy ( Openzeppelin v5 ) . I am following Patrick's video tutorial , in that tutorial , when he deployed using the deployProxy() method , 3 contracts got deployed ( i.e. Implementation , Proxy and Proxy Admin ) , but when I am trying to deploy using the same method , only 2 contracts got deployed . What am I doing wrong ?
This is the file that is getting generated when I call my deploy script .

This is my script file code

@iamgauravpant Can you please provide the following information:

  • What network did you deploy to and what are the addresses (please paste the addresses in text format)
  • The output of the following commands:
npm list @openzeppelin/hardhat-upgrades
npm list @openzeppelin/upgrades-core

I deployed on PolygonMumbai .
check these 2 transactions , when I ran deploy.js file , these 2 contracts got created . In Patrick's video , I also saw that the 2 of the deployed contracts was automatically named TransparentUpgradeableProxy and ProxyAdmin , but in my case I didn't see anything like that .

I guess this is my implementation contract ( this is an NFT contract ) :

And I guess this is the proxy contract :

check this image attached .

and if is the proxy address , I am not able to call any of the implementation's functions using it (functions like nft contract's name , safeMint etc. ) . What may be wrong there ? How can I confirm 0x902780f29c21a8ec2f852122ebb6884d107f2085 is indeed my proxy address ?

In the latest versions of Hardhat Upgrades, which you are using, it is deploying proxy contracts from OpenZeppelin Contracts 5.0.

In OpenZeppelin Contracts 5.0, TransparentUpgradeableProxy deploys its own ProxyAdmin in its constructor. Therefore, we no longer track the ProxyAdmin in the network file because each transparent proxy has its own admin. is your proxy contract. is your implementation contract.

You can see from the logs of the transaction which created your proxy contract, it has an AdminChanged event where the newAdmin is -- that is your ProxyAdmin contract (which was deployed by the TransparentUpgradeableProxy).

When calling your implementation's functions, you should submit the function calls to your proxy address, not your implementation address. To help with this, you may want to verify your source code -- see the verify task. Then from the block explorer, you will be able to interact with your contract from the proxy address using your implementation contract's ABI.

1 Like

@ericglau thank you very much .
I'm having issues in upgradeProxy() method now .
Below is the code for upfrading proxy implementation that I wrote .

When I run this , Proxy's implementation is getting upgraded , but I don't know if there are limitations to what can be upgraded ?
I started with a basic ERC721Upgradeable contract that we get from Openzeppelin wizard , called it NonFungibleTokenV1 ( name is NonFungibleTokenV1 , symbol is NFTV1 , and safeMint() function can only be called by owner of the NonFungibleTokenV1 contract that is passed in initializer function as parameter ) .
Then , I modified this NFT contract , called it NonFungibleTokenV2 ( name is NonFungibleTokenV2 , symbol is NFTV2 , and safeMint() function can now be called by any address i.e. I've removed the onlyOwner modifier from safeMint() ) . What I see now is the contract name is still returning NonFungibleTokenV1 but safeMint() is upgraded as expected i.e. anyone can call safeMint() now . Why didn't the contract name get updated to NonFungibleTokenV2 but safeMint() got updated as expected ?

0xCD2967764cF838c7F57967815Dd5a4b0650E82f9 [ NFT Proxy Address ]
0xaE654496322D1264bfa2F437cA6c8923699a8D26 [ ProxyAdmin Address ]

To call a function during upgrade, it should be like this (note the call: part)

  await upgrades.upgradeProxy(proxyAddress, MyImplementationV2, {
    call: { fn: 'myFunction', args: ['my function args] },

The way you wrote caused it to upgrade without calling any function.

However, you cannot call your initializer again if it is using the initializable modifier, since it was already initialized in the first version. You can consider using a reinitializer(version) modifier instead. See

1 Like

@ericglau I am upgrading proxy's implementation in a different way than it is shown in the article that you've referred here .
Here MyTokenV2 extends MyToken .
In my case NonFungibleTokenV2 is a completely new contract ( it does not extend NonFungibleTokenV1 like the way MyTokenV2 does in the article ) .

Below is NonFungibleTokenV1 :

Below is NonFungibleTokenV2 :

the way I've written v2 of implementation contract is okay or do I need to modify NonFungibleTokenV2 like MyTokenV2 ?
what may go wrong in my implementation ?
Is the article way recommend way to write different versions for implementation contracts using transparent upgradeable proxy ?

I am having one problem currently , I started with deploying a proxy for NonFungibleTokenV1 , then upgraded proxy's implementation to NonFungibleTokenV2 . But in polygon mumbai explorer it is still showing proxy contract's name as NonFungibleTokenV1 and contract's symbol as NFTV1 . It should rather show name as NonFungibleTokenV2 and symbol as NFTV2 .


ProxyAdmin :

Note that your initialize function in V2 is not protected so anyone can call it. That means you need to call it during upgrade, in the same transaction. Otherwise someone can overtake your proxy if they frontrun you in calling it.

From your contract, it doesn't look like it was upgraded though.

1 Like