Deploying Upgradeable Proxies and Proxy Admin from Factory contract

HI!

Is it possible to deploy Upgradable Proxies from a Factory contract?

What we need to achieve is have a Factory Contract that can deploy the ProxyAdmin, an Upgradable Proxy (Transparent or UUPS), and then transfer ownership to the deployer, and I have to do this at runtime.

Is this something that can be achieved? Are there examples I can use?

I’ve looked at the OZ contracts and found the Clones library but that is for non upgradable proxies, so would not cover my use case because I need the user to be able to update the implementation of these proxies after deployment.

Any advise would be highly appreciated! Thanks!

Regards,
Julian

This can be achieved but we don’t have any examples. I believe it should be quite simple to put together by importing the relevant contracts and just using Solidity’s new operator create the contract instances. If you want to put together this code and share it here I can review it!

@frangio Here is my initial attempt. Please let me know if its in the right direction.

It looks too easy to be true. I created two contracts the UpgradeableToken and the Factory contract which deploys the Tokens. I realized I don’t need a ProxyAdmin for UUPS.

One of the doubts I had was which type of “Proxy” should I use for UUPS. I went for ERC1967Proxy not sure if it’s the correct choice.

MyTokenUpgradeable

// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

import "@openzeppelin/contracts-upgradeable/token/ERC20/presets/ERC20PresetFixedSupplyUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";

contract MyTokenUpgradeable is
    ERC20PresetFixedSupplyUpgradeable,
    UUPSUpgradeable,
    OwnableUpgradeable
{
    function initialize(
        string memory name,
        string memory symbol,
        uint256 initialSupply,
        address owner
    ) public override initializer {
        __ERC20PresetFixedSupply_init(name, symbol, initialSupply, owner);
        __Ownable_init();
        transferOwnership(owner);
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}

MyTokenFactory

// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "./MyTokenUpgradeable.sol";

contract MyTokenFactoryUUPS {
    address immutable tokenImplementation;

    event TokenDeployed(address tokenAddress);

    constructor() {
        tokenImplementation = address(new MyTokenUpgradeable());
    }

    function createToken(string calldata name, string calldata symbol, uint256 initialSupply, address owner) external returns (address) {
        ERC1967Proxy proxy = new ERC1967Proxy(
            tokenImplementation,
            abi.encodeWithSelector(MyTokenUpgradeable(address(0)).initialize.selector, name, symbol, initialSupply, owner)
        );
        emit TokenDeployed(address(proxy));
        return address(proxy);
    }
}

One thing I wasn’t sure is if there is a better way to set the new owner for the Token, but seems like I have to do __Ownable_init so I can then transfer it to the provided owner. Any workaround to avoid the 2-step process?

Thank you very much for all your support!

Regards,
Julian

because you inherited from OwnableUpgradeable and it does not have constructor so you must call it’s initialize function. (initializer functions are not called automatically).
so owner will not set and you can’t upgrade because you use onlyOwner modifier in _authorizeUpgrade if you ommit __Ownable_init(); .

1 Like

Your code looks good to me!

There’s no way to avoid the 2-step ownership setup, but consider that the second time you set the owner the storage slot is warm so it will be cheaper to write. (Actually, does RSK have EIP-2929?)

1 Like

Thanks Fran for looking at the code! Im working fully on Ethereum now so this is great to know. Not sure about RSK but I think they did something different than EIP-2929.

1 Like