1

I am reading the paper: CLUE: Towards Discovering Locked Cryptocurrencies in Ethereum, available at: https://www.researchgate.net/publication/346425189_CLUE_Towards_Discovering_Locked_Cryptocurrencies_in_Ethereum It says that:

Contract-creation Failure EOA: When the user deploys a
smart contract in Ethereum, he/she will still receive one fake contract
address if the contract-creation fails. Indeed, the received
contract address does not exist in StateDB just after the contractcreation
failure. However, some users might wrongly ignore the failure
message and still transfer cryptocurrencies to the fake contract
address, leading to cryptocurrencies locked permanently. Because
the address with locked cryptocurrencies never stores code, we
classify it as EOA.

Is it possible to get a fake contract address? Is the above text correct?

Zulfi.

1 Answer 1

1

Maybe the word "fake" was not the correct one to use in that scenario.

Let's take a look at this way of deploying a contract from another contract taken from solidity-by-example:

    function deploy(bytes memory bytecode, uint _salt) public payable {
        address addr;

        /*
        NOTE: How to call create2

        create2(v, p, n, s)
        create new contract with code at memory p to p + n
        and send v wei
        and return the new address
        where new address = first 20 bytes of keccak256(0xff + address(this) + s + keccak256(mem[p…(p+n)))
              s = big-endian 256-bit value
        */
        assembly {
            addr := create2(
                callvalue(), // wei sent with current call
                // Actual code starts after skipping the first 32 bytes
                add(bytecode, 0x20),
                mload(bytecode), // Load the size of code contained in the first 32 bytes
                _salt // Salt from function arguments
            )

            if iszero(extcodesize(addr)) {
                revert(0, 0)
            }
        }

        emit Deployed(addr, _salt);
    }
}

Take special attention to the block of code:

if iszero(extcodesize(addr)) {
   revert(0, 0)
}

It's checking if the contract was deployed properly, by checking the contract code size with extcodesize (check the list of opcodes here).

If this check was not there and for some reason, the contract did not deploy correctly, we will have an address, but there's no code related to it.

The address of the contract is derived from the information of the deployer contract. So, in theory, we could generate or get the possible address of the contract without the contract being deployed. And if we send eth to that address before the contract is actually deployed, then we will not be able to deploy a contract to that address and we will lose the eth.

For example, check the following contract deployer:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

contract TestDeployContract {
    uint256 public counter = 1;

    receive() external payable {}
}

contract Contract {

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

    // Function to derive the address of a smart contract.
    function getPredictedContractAddress() 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), uint256(hash), keccak256(type(TestDeployContract).creationCode))
        );
        return address(uint160(uint(hash32)));
    }

    function deploy() 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);
    }

     function deployWithAssembly() public {
        uint256 _salt = uint256(hash);
        address addr;
        bytes memory bytecode = type(TestDeployContract).creationCode;

        assembly {
            addr := create2(
                callvalue(), // wei sent with current call
                // Actual code starts after skipping the first 32 bytes
                add(bytecode, 0x20),
                mload(bytecode), // Load the size of code contained in the first 32 bytes
                _salt // Salt from function arguments
            )

            if iszero(extcodesize(addr)) {
                revert(0, 0)
            }
        }

        testDeployContractAddressFromAssembly = addr;

    }

}

If you call the getPredictedContractAddress() function, you will get the address of the contract that deploy() or deployWithAssembly() would create and return. So, if we call getPredictedContractAddress() before deploying the contract, and we check the returned address in etherscan, it will look like a regular, valid address. We can send eth to it. But we don't have a private key for it, it's just a valid address format but it does not belong to anyone, and it's not a contract. If we send eth to that address, we will not be able to deploy the contract to that address and we will lose the eth.

I did it. Check:

enter image description here

https://rinkeby.etherscan.io/address/0xb2216681F3bA09280E5D018f1b7C03D532bF55dc

I sent some test eth to it. And then, I call the deploy() function, and it assigned the 'derived' address to the state variable so I can see it:

enter image description here

But guess what, the contract deployment did not fail and the actual address at which the address deployed was not the expected one, because it was busy/used already, so it was actually deployed here at address 0x26b76b352b0472f1494bc8e740cb0452fe516ed2, but the deploy() function says it was deployed at 0xb2216681F3bA09280E5D018f1b7C03D532bF55dc. Really bad.

Check the deployed contract: https://rinkeby.etherscan.io/address/0x26b76b352b0472f1494bc8e740cb0452fe516ed2

So, now what I thought was the future address of my contract now it's just a regular EOA, and since I already sent eth to it before deploying the contract, I'm not able to recover those eth, they are lost.

So, this is one of the things we need to be careful with while deploying contracts.

2
  • thanks. EOA account is not associated with a Smart Contract (SC) and If the above code deals with EOA why are you checking the SC's deployment?
    – zak100
    Sep 23 at 13:05
  • 1
    The above code does not deal with EOA. It just shows one possible pitfall when creating smart contracts, some things could go wrong and funds could be lost. I mentioned the EOA because an address was supposed to be for a contract, but since it was first used as a EOA (probably accidentally in the real world), now the contract cannot be deployed to that address and funds may be lost. The same can happen if you try to deploy a contract and you don't realize the deployment failed but you still try to send ether to the address of that "contract", you lose the funds. Sep 23 at 13:49

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.