3

Let's consider two scenarios:

  1. Address X deployed Uniswap code to ETH mainnet with Create2 opcode to address Y
  2. Address X deployed Uniswap code to ETH mainnet with Create opcode to address Z

I am an unrelated wallet at address A, and I'd like to deploy Uniswap to EVM Chain E (where Uniswap code has not been deployed).

Knowing how create and create2 work, how easy would it be for an unrelated actor at address A to deploy the Uniswap contract to EVM Chain E and deterministically choose either address Y or address Z even if they are not the original sender (i.e. address X)? Can Address A "squat" on Uniswaps address with uniswap code on EVM Chain E?

1 Answer 1

3

Let me see if I understand your need...

So, you would like to know how hard it would be for anybody (other than the contract owner) to deploy a contract in another EVM-compatible chain with the same contract address as an existing one in another chain?

Short answer:

It would take on average 2**256 tries to make a salt for the EVM to create a contract with the desired address, since create2 uses the address of the creator contract to mix it with the creationCode and the salt to produce the new contract address. Meaning that the person would need, in the other chain, to have a private key that generates a public key that generates an address/account exactly the same as the one in the original chain, that makes transactions until it has the nonce set to the nonce the original user had at the moment of creating his smart contract, and that smart contract itself makes sure to have the correct nonce like at the time the original created its smart contract or if it used create2 then use the same settings and salt the original contract used. So, practically, it should be the exact same person, because we all know that guessing a private key is not feasible.

Long answer:

The create2 opcode does not accept an arbitrary address as input, not even when creating it like new TestDeployContract{salt: hash}(). It takes the address of the creating contract and there's nothing we can do to force it to take an arbitrary address that we would like.

So, the only control over the create2 opcode that we have is the bytecode of the contract and the salt, and in your case, we want to deploy the exact same bytecodes as in the original chain, so changing this is not an option.

We know that the keccak256 hash produces a 256-bit number from the given data. The EVM create2 uses keccak256 mixing it with the contract bytecode, the creator contract's address, and the salt to derive the new contract address. If somehow we could pass a salt, a random number, that could make the keccak256 hash of the create2 produce the same hash as the one produced in the original chain.

We all know that this would be brute force. Trying to make keccak256 create the same output given different inputs is unfeasible. So, on average, it would take 2**256 tries, generating a random salt, and trying to calculate the address manually to see if it's the same as the one we want.

So, answering your question, yes, it would be hard, really hard, 2**256 hard, or at least 2**160, since the algorithm copies the last 20 bytes of the keccak256 to use it as the address. So, in theory, we would need to try, brute force, until the last 20 bytes of the hash are what we are expecting.

Some code with some examples and comments:

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

contract TestDeployContract {
    uint256 public id = 50;
}

contract Contract {

    // Using keccak256 hash of the contract name to use it as the salt
    bytes32 hash = keccak256("TestDeployContract");
    uint256 hashValue = uint(keccak256("TestDeployContract"));
    address public testDeployContractAddress;

    // Function to derive the address of a smart contract.
    function getAddress() public view returns (address) {
        // Passing address(this) manually here. But we have no option to provide any address we want when actually deploying the contract with create2
        bytes32 hash32 = keccak256(
            abi.encodePacked(bytes1(0xff), address(this), hashValue, keccak256(type(TestDeployContract).creationCode))
        );
        return address(uint160(uint(hash32)));
    }

    function createContract() public {
        // No option provide any creator address we want when actually deploying the contract
        // So it uses the address of the contract that is executing this code,
        // mix it with the bytecode of the TestDeployContract and the salt to produce a
        // keccak256 hash and derive the new contract address from it
        TestDeployContract testDeployContract = new TestDeployContract{salt: hash}();
        testDeployContractAddress = address(testDeployContract);
    }

}
4
  • 1
    This is what I was thinking, and I can't think of a way for someone to squat on the contract address. Sounds like it's not impossible but pretty near impossible. Aug 22, 2022 at 19:43
  • 2
    Good answer. However an address is 20 bytes, so all those 2**256 are actually 2**160. Also there are some edge cases: if there's a factory of addresses someone can create contracts with the same address on other chains. For example: gov.optimism.io/t/message-to-optimism-community-from-wintermute/…
    – 0xSanson
    Aug 22, 2022 at 20:55
  • 1
    Hmmm, good point. Should we update @jeremy's answer with 2**160? Or is it even lower than that? Aug 22, 2022 at 21:48
  • 1
    That sounds good! Even though the EVM create2 opcode will be creating keccak256 hash, only the last 20 bytes need to be what the attacker is expecting. So, the probability a keccak256 hash that ends with 20 bytes that the attacker is expecting, is indeed 2*160. Which is still a lot to make it feasible. I updated the description of my answer a bit. Aug 22, 2022 at 21:55

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.