ERC1967Proxy problem with constructor in Remix

Hello,

I am trying to build a factory InterviewProxy using ERC1967Proxy. I am using Remix for deployment.
My implementation contract is Interview.Sol which is a UUPSUpgradeable.
First I deploy Interview and then try to pass its address as _Delegate to the constructor of InterviewProxy.
I am not sure what to pass for _Data. What is _Data for? I tried and passed some random Byte data, but I still got the error "Address: low-level delegate call failed".
Please let me know if you see any other issue with my proxy factory. Thanks.
I appreciate any help.

Here is my code:
'''
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.11;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/ERC1967/ERC1967Proxy.sol";

import "https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/OwnableUpgradeable.sol";

import "./Interview.sol";

contract InterviewProxy is ERC1967Proxy, OwnableUpgradeable {

Interview[] private deployedInterviews;

constructor (address _delegate, bytes memory _data  )  ERC1967Proxy(_delegate, _data)  

{

    __Ownable_init();

}

function createInterview(uint reward_per_finalist, uint number_of_finalists, uint number_of_interviews) public payable

{

    Interview interview = Interview(_getImplementation());

    interview.initialize{value: msg.value}(owner(), msg.sender, reward_per_finalist, number_of_finalists, number_of_interviews);

    deployedInterviews.push(interview);

}

function getDeployedInterviews() public view returns (Interview[] memory) {

    return deployedInterviews;

}

}
'''

1 Like

Hi @champ22,

Your proxy factory itself does not need to extend ERC1967Proxy since it is not a proxy.

_data is an encoded function call to your implementation contract's initializer, and this encoded function call should include the initializer function name and its arguments. This allows the proxy to be initialized at the same time as it is deployed.

See Deploying Upgradeable Proxies and Proxy Admin from Factory contract for some examples of proxy factories.

1 Like

Thank you Eric. Understood.

Another quick question, I like my BuildInterview() function to be payable because my initializer() function is payable. Is there a way that I can pass {value: msg.value} in abi.encodeWithSelector(Interview(address(0)).initialize.selector, tx.origin, msg.sender, reward_per_finalist, number_of_finalists, number_of_interviews)?

Thanks again!

'''
contract InterviewFactory {

address immutable InterviewImplementation;

address[] private deployedInterviews;

event InterviewDeployed(address InterviewAddress);

constructor(){

InterviewImplementation = address(new Interview());

}

function BuildInterview(uint reward_per_finalist, uint number_of_finalists, uint number_of_interviews) public payable {

    ERC1967Proxy proxy = new ERC1967Proxy(

        InterviewImplementation,

        abi.encodeWithSelector(Interview(address(0)).initialize.selector, tx.origin, msg.sender, reward_per_finalist, number_of_finalists, number_of_interviews)

        );

   

    emit InterviewDeployed(address(proxy));

    deployedInterviews.push(address(proxy));

}

function getDeployedInterviews() public view returns (address[] memory) {

    return deployedInterviews;

}

function getFactoryImplementation() public view returns (address) {

    return InterviewImplementation;

}

}
'''

Hello @champ22

The ERC1967Proxy constructor is payable, so AFAIK, you could do

ERC1967Proxy proxy = new ERC1967Proxy{value: msg.value}(
    InterviewImplementation, 
    abi.encodeCall(Interview.initialize, (tx.origin, msg.sender, reward_per_finalist, number_of_finalists, number_of_interviews))
);

This will create the proxy with value sent at construction. The proxy will set itself up, including a delegate call with to the Interview.initialize function. Thanks to the delegate call semantics, this you can use msg.value in the this initialize function to retrieve the value transfer. Possibly you could also use address(this).balance (this might even be better, just in case the address already hold eth.

1 Like

Thank you so much Eric!