Error on upgrade a proxy Erc721

Hello,

I want to upgrade my Erc721 proxy contract, but I get a revert error

I just want to change safeMint method to pass tokenId

I created a test on foundry

But it failed with

│   │   ├─ [258] Beamon::upgradeToAndCall(BeamonV2:[0x15C725dc60aCEc8919cad03DA9F6e3759D076824], 0x) [delegatecall]
│   │   │   └─ ← "EvmError: Revert"

All the source code is on the github I shared, I think I miss something but I don't know what.

Thanks for your advise

Please specify which OZ version you have used in your old contract, and which OZ version you are using in your new contract.

In addition, please note that your new contract is missing an initialization step which should probably be similar (or even identical) to the one implemented in your old contract:

    function initialize(
        address defaultAdmin,
        address minter,
        string calldata _uri
    ) public initializer {
        __ERC721_init("Beamon", "BM");
        __ERC721Burnable_init();
        __AccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
        _grantRole(MINTER_ROLE, minter);
        uri = _uri;
    }

You MUST keep the same layout structure: storage variables should be placed in the same order and required functions such as constructor and initialize should not be removed/updated.

Only copy your V1 implementation (Beamon.sol), create a new .sol file (BeamonV2.sol) and paste it. After that update it accordingly - it's content should be the same as:

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

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract BeamonV2 is
    Initializable,
    ERC721Upgradeable,
    ERC721BurnableUpgradeable,
    AccessControlUpgradeable
{
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    uint256 private _nextTokenId;
    string public uri;

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

    function initialize(
        address defaultAdmin,
        address minter,
        string calldata _uri
    ) public initializer {
        __ERC721_init("Beamon", "BM");
        __ERC721Burnable_init();
        __AccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
        _grantRole(MINTER_ROLE, minter);
        uri = _uri;
    }

    function _baseURI() internal view override returns (string memory) {
        return uri;
    }

    function safeMint(
        address to,
        uint256 tokenId
    ) public onlyRole(MINTER_ROLE) {
        _safeMint(to, tokenId);
    }

    function updateUri(string calldata newUri) public onlyRole(MINTER_ROLE) {
        uri = newUri;
    }

    // The following functions are overrides required by Solidity.

    function supportsInterface(
        bytes4 interfaceId
    )
        public
        view
        override(ERC721Upgradeable, AccessControlUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

Oh it seems the proxyadmin in the contract didn't match the address of proxyadmin I used

I understand my mistake, the transparent proxy create is own proxyadmin for the initial owner

  /**
     * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
     * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
     * {ERC1967Proxy-constructor}.
     */
    constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
        _admin = address(new ProxyAdmin(initialOwner));
        // Set the storage value and emit an event for ERC-1967 compatibility
        ERC1967Utils.changeAdmin(_proxyAdmin());
    }

_admin = address(new ProxyAdmin(initialOwner));

It's a big change, on the old transparent proxy we passed directly the admin to the contract, so we can't share anymore a proxyAdmin between different proxy contract

So my contract was deployed with a ProxyAdmin owned by an other ProxyAdmin, I don't think I can't do something about it ... fortunately is deployed only on testnet.

1 Like