Manual Deploy: ERC1967Proxy + Remix (to understand how it's done)

Hey! I'm trying to understand how the deployment of a UUPSUpgradeable contract works.

I created a simple one using the openzeppelin wizard:

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

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.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 MyToken is Initializable, ERC721Upgradeable, ERC721URIStorageUpgradeable, OwnableUpgradeable, UUPSUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize() initializer public {
        __ERC721_init("MyToken", "MTK");
        __ERC721URIStorage_init();
        __Ownable_init();
        __UUPSUpgradeable_init();
    }

    function safeMint(address to, uint256 tokenId, string memory uri)
        public
        onlyOwner
    {
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    function _authorizeUpgrade(address newImplementation)
        internal
        onlyOwner
        override
    {}

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId)
        internal
        override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
    {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }
}

I know that with Remix, it is possible to use the "Deploy with proxy" function that magically does everything. I also know that there are tools to automate. But I want to understand why manually I can't. How to build and initialize the proxy?

I noticed that Remix uses openzeppelin code (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/ERC1967/ERC1967Proxy.sol) as a Proxy.

So I did the same thing. First I deployed my contract (MyToken). Then I deployed the ERC1967Proxy using the constructor parameters:
_LOGIC: the address of the MyToken contract
_DATE: 0x

And then it doesn't work :frowning:

What was left to do?

That should work.
NOTE: When deploying the proxy, also set _data with calldata for the initializer as explained in the thread below.

After deploying the proxy, you need to attach the implementation to the proxy address so that you can use the implementation's ABI in Remix.

You can do that with "At Address" from your implementation (MyToken).

Hi @ericglau !

Apparently, I already had a problem in deploying the proxy. The log is "pending...". Then using your suggestion the owner is zeroed as if it had not been initialized.
I'm forgetting something?

Screenshot_20230327_181825

Maybe call the calldata with some data to initialize the proxy and change the owner. But if so, what value?

Screenshot_20230327_182422

When you deploy the proxy, you can set _data to 0x8129fc1c which is the encoded function signature for initialize. Or call the calldata with 0x8129fc1c using Transact.

1 Like

Thanks @ericglau! It works!

I was reading the constructor doc but I wasn't finding the correct value for _DATA. Is this a constant? I didn't find the value in the code. How did you generate this value?

It is not a constant, but ABI encoded calldata for the initializer function (along with its arguments) that you want to call.

This is specified in https://docs.soliditylang.org/en/develop/abi-spec.html
You can use a tool like https://abi.hashex.org/ to generate the encoded data.

For example, since your implementation has the initializer as "initializer()" without arguments, you would just encode the function name "initializer". If it has arguments, you would add all of the functions to the encoding as well.

1 Like

@ericglau, thanks for the perfect answer!

A post was split to a new topic: Manually deploying UUPS proxy in Hardhat