How to verify uups proxy deployed with remix on zkevm polygon

Dear all,

I have deployed the following contracts (UUPS proxy and implementation) on zkEVM mainnet using remix (0.8.20).
The source code comes from openzeppelin wizzard.
The contract has been slighly modified (adding max supply and blacklisting).

I managed to verify the implementation contract (using both etherscan plugin on remix or zkevm.polygonscan) but i did not manage to verify the proxy contract.

Where can i find please the exact code used by remix to create the proxy contract and the exact constructor argument ? this in order to follow the verification procedure on zkevm.polygonscan ?

(as remix etherscan plugin doesn't seem to work for proxy)

(I did follow all the steps on etherscan remix plugin and was stoppped at the point where I get a GUID for the proxy and a green message "successfully updated, but no verification appears on zkevm.polygonscan contract page)

Many thanks for your help !

Here are the adresses on zkEVM, following is the unflattened contract.

Proxy
0xCC2dDa901413c3BE377b09e999DB0191d38B4764
1st implementation
0x34ed3a8320562A7ecFbD6365F300304857B8A729

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

 /// @custom:security-contact contact@cheerbitcoin.org

contract CheerBitcoin is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable, OwnableUpgradeable, UUPSUpgradeable {
   
    uint256 public constant maxSupply = 2100000000 * (10 ** 18);
   
    mapping(address => bool) private _blacklisted;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(address initialOwner) initializer public {
        __ERC20_init("CheerBitcoin", "C");
        __ERC20Burnable_init();
        __ERC20Pausable_init();
        __Ownable_init(initialOwner);
        __UUPSUpgradeable_init();
    }

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    function mint(address to, uint256 amount) public onlyOwner {

        require(totalSupply() + amount <= maxSupply, "Max supply exceeded");

        _mint(to, amount);

    }

    function _authorizeUpgrade(address newImplementation)
        internal
        onlyOwner
        override
    {}

    // The following functions are overrides required by Solidity.

    function _update(address from, address to, uint256 value)
        internal
        override(ERC20Upgradeable, ERC20PausableUpgradeable)
    {
        super._update(from, to, value);
    }

    // The following functions implement blacklisting.

    function addToBlacklist(address account) public onlyOwner {

        _blacklisted[account] = true;

    }

    function removeFromBlacklist(address account) public onlyOwner {

        _blacklisted[account] = false;

    }

    function isBlacklisted(address account) public view returns (bool) {

        return _blacklisted[account];

    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {

        require(!_blacklisted[msg.sender], "Sender is blacklisted");

        require(!_blacklisted[recipient], "Recipient is blacklisted");

        return super.transfer(recipient, amount);

    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {

        require(!_blacklisted[sender], "Sender is blacklisted");

        require(!_blacklisted[recipient], "Recipient is blacklisted");

        return super.transferFrom(sender, recipient, amount);

    }
}

Many thanks to Yann Levreau for having help solve my problem.

For those interested, here are the hints from Yann :

"We are using either https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/proxy/ERC1967/ERC1967Proxy.sol (with soljson-v0.8.7+commit.e28d00a7.js) or https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.0/contracts/proxy/ERC1967/ERC1967Proxy.sol (and soljson-v0.8.21+commit.d9974bed.js), with optimize false and evmVersion to the default."

I managed to verify my proxy contract with zkevm.polygonscan by flattening the source code of the V5 proxy on remix (don't forget to precise the exact path of the openzeppelin library), using this flattened code in zkem.polygonscan verifying page. The ABI constructor argument where put automatically by zkevm.polygonscan

Best of luck to all of you in developping and verifying proxies...

1 Like

And many thanks to @ericglau whose answers to other popular posts on this topic helped me understand what to do.