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?
7 Answers
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: 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.
-
9As 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 useEXCODESIZE
to prevent other contracts from interacting with yours, it must be used with caution. Commented Aug 18, 2018 at 13:20 -
1I 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. Commented Feb 2, 2019 at 14:29 -
After 0.8.1 one can also use this snippet without loss of efficiency (though one should still take the same aforementioned caution):
function isContract(address addr) private returns (bool isContract) { return addr.code.length > 0; }
– shamisenCommented Jan 3, 2022 at 12:55 -
+1 Thanks! I see this in WUST contract on BSC chain, and I wonder myself. For reference of real usage in real project bscscan.com/address/…– haxporCommented Apr 9, 2022 at 4:13
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.
UPDATE
Address.sol
in OpenZeppelin/contracts/utility has function isContract
that works on the principle described. Same warning applies. Use with awareness.
-
1As @eth below says, we can use require(msg.sender == tx.origin). Many says it has security vulnerabilities. I am sure it would be for other cases. But in my case, I just want to know caller is contract or not. I dont want other contracts from interacting with my contract whatsoever. so is it safe to use this, just for this use case? Thank again in advance for your time! Commented Mar 24, 2019 at 15:23
-
1The answer is very good, in my opinion. Also the source is very respectablr. I would heed the warnings. Commented Mar 24, 2019 at 17:00
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.
Update for Solidity v0.8 and Above
pragma solidity >=0.8.0;
function isContract(address _addr) view returns (bool) {
return _addr.code.length > 0;
}
Note that all security caveats mentioned in the other comments still apply.
I'm working on this as well. There is an opcode called extcodehash
. It says
The EXTCODEHASH of the account without code is c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 what is the keccack256 hash of empty data
So I think there is a possibility to check isContract
by using this extcodehash
combined with the c5d2460186f72...
function isContract(address addr) internal view returns (bool) {
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
bytes32 codehash;
assembly {
codehash := extcodehash(addr)
}
return (codehash != 0x0 && codehash != accountHash);
}
This means, if the code hash does not equal to 0 nor c5d2460186f72...
, we can conclude this is the contract address?
-
We would need a test to see if we can trick your approach with a constructor.– SkyCommented Jul 11, 2022 at 6:06
One obvious point not emphasized in previously posted answers is that, YES, requiring
assembly {
size := extcodesize(_addr)
}
will guarantee that only a contract can make it past the check if size > 0
.
However, the opposite check to see that a sender is NOT a contract (but an EOA) is much more complicated and requires a signature verification scheme in order to prove. There are 2 approaches, which you can read up on more here and here.
The best way to check and make sure that an address is not a contract, is by comparing tx.origin with msg.sender. You could do a modifier for that.
modifier onlyEoa() {
require(tx.origin == msg.sender, "Not EOA");
_;
}
address.code.length > 0
is not always a good solution because if a contract calls your contract from its constructor, then address.code.length
will be 0 because the attacking contract has not been constructed yet, tricking you into thinking that is not a contract.
More info in this response: Is there any simple function done in Solidity to check if an address is a contract address or a wallet address?