ProxyAdmin but not TransparentUpgradeableProxy


Please forgive me if I have missed something basic! I've created an upgradable ERC20 using the Wizard, which I have tested extensively on Rinkeby including upgrading, and so I have now deployed to the Ethereum mainnet.

However, on deploy (using the exact same code as used on Rinkeby) the Implementation was deployed and a ProxyAdmin. I was expecting an Implementation and a TransparentUpgradeableProxy to be deployed. The deploy also hasn't initialized my implementation of the first deploy. On Rinkeby I would run the deploy script, run the script to verify the implementation, then verify the TransparentUpgradeableProxy in etherscan.

How do I deploy a TransparentUpgradeableProxy for the implementation, or how to initialize, or have I missed a step?

Thank you kindly for your future responses!

:1234: Code to reproduce

// echo.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract Echo is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, PausableUpgradeable, OwnableUpgradeable, UUPSUpgradeable {

    function initialize() initializer public {
        __ERC20_init("Echo", "ECHO");

        _mint(msg.sender, 1000000000 * 10 ** decimals());

    function pause() public onlyOwner {

    function unpause() public onlyOwner {

    function version() pure virtual public returns (string memory) {
        return "1.0.0";

    function _beforeTokenTransfer(address from, address to, uint256 amount)
        super._beforeTokenTransfer(from, to, amount);

    function _authorizeUpgrade(address newImplementation)

// deploy-echo.js
const { ethers, upgrades } = require('hardhat');

async function main () {
    const [deployer] = await ethers.getSigners();
    console.log("Deploying contracts with the account:", deployer.address);

    const gasBefore = await deployer.getBalance();
    console.log("Account balance:", ethers.utils.formatEther(gasBefore));

    const Echo = await ethers.getContractFactory('Echo');
    console.log('Deploying Echo...');

    const echo = await upgrades.deployProxy(Echo, { initializer: 'initialize' });
    await echo.deployed();
    console.log('Echo Proxy deployed to:', echo.address);

    const implementationAddress = await upgrades.erc1967.getImplementationAddress(echo.address);
    console.log('Echo Implementation deployed to:', implementationAddress);

    const gasAfter = await deployer.getBalance();
    console.log("Account balance:", ethers.utils.formatEther(gasAfter));
    console.log("Gas cost:", ethers.utils.formatEther(gasBefore - gasAfter));

    .then(() => process.exit(0))
    .catch((error) => {

:computer: Environment

Deployed by calling:

npx hardhat run scripts/deploy-echo.js --network mainnet

This ended up being a timeout error:
TransactionMinedTimeout [Error]: Timed out waiting for transaction 0x...

When deploying the Implementation and ProxyAdmin contracts deployed but timed out during the TransparentUpgradeableProxy.

I was very hesitant to re-run as it already cost 0.3 ETH in gas, but the solution was to bite the bullet and re-run the same deploy script. This ignored the already deployed contract and only deployed the TransparentUpgradeableProxy.

1 Like