16

Is it possible, from within a contract written in Solidity, to check if a contract is placed on a specific address or if this address does not contain any code?

22

This works:

function isContract(address _addr) private returns (bool isContract){
  uint32 size;
  assembly {
    size := extcodesize(_addr)
  }
  return (size > 0);
}

The assembly language that all Ethereum contracts compile down to contains an opcode for this precise operation: EXTCODESIZE. This opcode returns the size of the code on an address. If the size is larger than zero, the address is a contract. But you need to write assembly code within the contract to access this opcode since the Solidity compiler does not support it directly at the moment. The above code creates a private method that you can call from within your contract to check if another address contains code. If you don't want a private method, remove the private keyword from the function header.

Edit: It turns out that EXTCODESIZE returns 0 if it is called from the constructor of a contract. So if you are using this in a security sensitive setting, you would have to consider if this is a problem.

  • 2
    As far as I'm aware, this is the best method we have for checking whether an address is a contract right now. But anyone using this solution should also know that it's possible for a contract to return 0 from EXTCODESIZE if the function call was made from within that contracts constructor. This means that you can't blindly use EXCODESIZE to prevent other contracts from interacting with yours, it must be used with caution. – AnAllergyToAnalogy Aug 18 '18 at 13:20
  • I originally needed this since I had created my own ERC20 token contracts and some financial contracts that transferred these tokens. When the financial contracts where settled, they were deleted by calling selfdestruct. As a security measure, I did not want the ERC20 to be transfer funds to deleted financial contracts so I introduced this check. For this use case, this check works but for other use cases, the Rob Hitchens B9lab warning should be heeded. – Thorkil Værge Feb 2 at 14:29
2

Full credit to @AnAllergyToAnalogy for the caution item.

I made an example to demonstrate that a constructor will trick this method. Posting for others who might come across this thread.

In practice, isContract can't reliably detect an attacker calling from a constructor.

pragma solidity 0.4.25;

contract Victim {

    function isContract() public view returns(bool){
      uint32 size;
      address a = msg.sender;
      assembly {
        size := extcodesize(a)
      }
      return (size > 0);
    }

}

contract Attacker {

    bool public iTrickedIt;
    Victim v;

    constructor(address _v) public {
        v = Victim(_v);
        // addrss(this) doesn't have code, yet
        iTrickedIt = !v.isContract();
    }
}
  • deploy Victim
  • deploy Attacker with Victim address
  • check iTrickedIt in Attacker

Hope it helps.

0

Since this is security related, it's helpful to emphasize https://stackoverflow.com/questions/37644395/how-to-find-out-if-an-ethereum-address-is-a-contract:

The top-voted answer with the isContract function that uses EXTCODESIZE was discovered to be hackable.

The function will return false if it is invoked from a contract's constructor (because the contract has not been deployed yet).

The code should be used very carefully, if at all, to avoid security hacks such as:

https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide (archive)

To repeat:

Do not use the EXTCODESIZE check to prevent smart contracts from calling a function. This is not foolproof, it can be subverted by a constructor call, due to the fact that while the constructor is running, EXTCODESIZE for that address returns 0.

See sample code for a contract that tricks EXTCODESIZE to return 0.


If you want to make sure that an EOA is calling your contract, a simple way is require(msg.sender == tx.origin). However, preventing a contract is an anti-pattern with security and interoperability considerations.

This will need revisiting when account abstraction is implemented.

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.