1

So I have created an 'implementation contract' and a 'clone factory' which creates clones of the implementation contract. I have tested the functionality of the CloneFactory and it works in Remix.

However, I am now writing tests for the contract in Hardhat and it doesn't seem to be working correctly (Mocha and Chai). The issue I am facing is that the cloned contract is not being correctly initialised.

Here is my Clone Factory:

contract CloneFactory {
    address public implementationContract;
    address[] public allClones;

    event NewClone(address indexed _instance);

    mapping(address => address) public list;

    constructor(address _implementation) {
        implementationContract = _implementation;
    }

    function createClone(address _creator) payable external returns(address instance) {
        instance = Clones.clone(implementationContract);
        (bool success, ) = instance.call{value: msg.value}(abi.encodeWithSignature("initialize(address)", _creator));
        allClones.push(instance);
        list[_creator] = instance;
        emit NewClone(instance);
        return instance;
    }

Here is the initialiser function in my Implementation Contract:

   function initialize(address _creator) public initializer {
        VRFConsumerBaseV2.initialise(vrfCoordinatorV2);
        i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
        creator = _creator;

Here is the test I am running (this is where I believe there is an issue):

it("Initialises contract correctly", async function () {

        Main = await ethers.getContractFactory("Main");
        main = await Main.deploy();
        await main.deployed();

        const dir = path.resolve(
            __dirname,
            "/Users/xxx/hardhat-smartcontract-main/artifacts/contracts/Main.sol/Main.json"
          )
        const file = fs.readFileSync(dir, "utf8")
        const json = JSON.parse(file)
        const abi = json.abi

        CloneFactory = await ethers.getContractFactory("CloneFactory");
        cf = await CloneFactory.deploy(main.address);
        await cf.deployed();
    
        let currentProvider = new web3.providers.HttpProvider('http://localhost:8545');
        let web3Provider = new ethers.providers.Web3Provider(currentProvider);
        const [addr1, addr2] = await ethers.getSigners();

        const tx = await cf.createClone(addr1.address, {    
          value: ethers.utils.parseUnits("0", "ether")
        });

        const txReceipt = await tx.wait(1)


        const cloneAddress = await txReceipt.events[0].args._instance
        const clonedContract = new ethers.Contract(cloneAddress, abi, web3Provider)
        const accountConnectedClone = clonedContract.connect(addr1)
        const creator = await accountConnectedClone.getCreator()
        expect(addr1.address).to.equal(creator)
    })

When I run this test, I get the following output:

AssertionError: expected '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb…' to equal '0x00000000000000000000000000000000000…'
      + expected - actual

      -0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
      +0x0000000000000000000000000000000000000000

Basically, the creator is not being saved as the address of the caller of the 'createClone' function (which means it is not being initialized. I don't think there is an issue with the Clone Factory since this works correctly in Remix.

My suspicion is this has something to do with either the ABI or with the ethers/web3 provider, however I could be wrong.

There doesn't seem to be much info online about testing clone or proxy smart contracts so I hope someone here can help me out.

Thanks in advance!

EthereumBoy

1 Answer 1

0

I would add this line in createClone after the (bool success, ) = ... line:

require(success, "initialize failed");

You're calling initialize forwarding msg.value, but initialize isn't payable, so I would fix by either adding payable here

function initialize(address _creator) public payable initializer {

or by removing {value: msg.value} in the call.

I don't think this was the reason the call failed, since you're using 0 value but it's good to fix it.

3. Here _creator was never defined, doesn't the compiler give you a warning or error? It should probably be _whoopyCreator.

(bool success, ) = instance.call{value: msg.value}(abi.encodeWithSignature("initialize(address)", _creator));

Try implementing the above three suggestions, hopefully they help.

1
  • Hey! Tried the top two....not working. Also, in the contract it's defined as whoopyCreator. I just changed the values in the code here to make it more generic (missed removing the whoopy in the initialiser function but just did that). Commented Sep 9, 2022 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.