2

I'm trying to deploy a contract, delete it, and then deploy it again using CREATE2 with hardhat-deploy's deterministic feature.

However, I can't seem to get my contracts to have the same address. Here are my two contracts:

Box:

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

contract Box{
    function version() public pure returns (uint256) {
        return 1;
    }
    function destroy() public {
        selfdestruct(payable(msg.sender));
    }
}

BoxV2:

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

contract BoxV2{
    function version() public pure returns (uint256) {
        return 2;
    }
}

And here is my deploy and "upgrade" (destroy, and re-deploy deterministically) script:

module.exports = async ({ getNamedAccounts, deployments }) => {
    const { deploy } = deployments
    const { deployer } = await getNamedAccounts()

    const box = await deploy("Box", {
        from: deployer,
        args: [],
        deterministicDeployment: "0x1234",
    })

    const boxContract = await ethers.getContract("Box")
    await boxContract.destroy()
    
    const boxV2 = await deploy("BoxV2", {
        from: deployer,
        args: [],
        deterministicDeployment: "0x1234",
    })
    console.log(`Box address ${box.address}`)
    console.log(`BoxV2 address ${boxV2.address}`)
}

However, I get different addresses:

Box address 0x5c5457177d92B27981809C6B5a3C960E69a2Cb23
BoxV2 address 0xBCf2C4fE08453C6898E7C18eFE4AF24521196622

1 Answer 1

5
+50

In order to use Create2 and get the same address, you have to pass the same variables:

create2(someValueToPreventCollisionWithCreate, msg.sender, salt, contractBytecode)

So if your contractBytecode is different in create2, you'll not get the same address. To make a metamorphic contract, you'll have to deploy a proxy contract that will point to your different code. Something like this:

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

// See import "@openzeppelin/contracts/proxy/Proxy.sol"; for full example
import "@openzeppelin/contracts/proxy/Proxy.sol";

contract MetamorphicProxy is Proxy {
    // Don't do this!!
    // You'll get storage clashes!
    // address public s_implementation;

    // This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1
    bytes32 private constant _IMPLEMENTATION_SLOT =
        0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    function setImplementation(address newImplementation) public {
        assembly {
            sstore(_IMPLEMENTATION_SLOT, newImplementation)
        }
    }

    function _implementation() internal view override returns (address implementationAddress) {
        assembly {
            implementationAddress := sload(_IMPLEMENTATION_SLOT)
        }
    }

    function destroy() public {
        selfdestruct(payable(msg.sender));
    }
}

And then, destroy the proxy contract and redeploy it with a different implementation address to wipe storage and redeploy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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