How to find out if an Ethereum address is a contract?
Solution 1
Edit: Solidity has changed since this answer was first written, @manuel-aráoz has the correct answer.
There is no way in solidity to check if an address is a contract. One of the goals of Ethereum is for humans and smart contracts to both be treated equally. This leads into a future where smart contracts interact seamlessly with humans and other contracts. It might change in the future , but for now an arbitrary address is ambiguous.
Solution 2
Yes you can, by using some EVM assembly code to get the address' code size:
function isContract(address addr) returns (bool) {
uint size;
assembly { size := extcodesize(addr) }
return size > 0;
}
Solution 3
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.
Checking if a caller is a contract
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.
require(msg.sender == tx.origin)
will need revisiting when account abstraction is implemented.
Checking if a callee is a contract
As @Luke points out in a comment, there is no general on-chain way to find out about a callee. If you want to "call" an address, there's no general way to find out if that address is a contract, EOA, or an address that a new contract can be deployed on, or if it's a CREATE2 address.
One non-general way that works for some callees: you can have a mapping on-chain that stores addresses of known EOAs or contracts. (Just remember that for an address without any on-chain history, you can't know if it's an EOA or an address that a contract can be deployed on.)
Solution 4
This isn't something you can query from within a contract using Solidity, but if you were just wanting to know whether an address holds contract code or not, you can check using your geth console or similar with eg:
> eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f")
with the hex string (here 0xbfb2e296d9cf3e593e79981235aed29ab9984c0f
) as the address you wish to query. This will return the bytecode stored at that address.
You can also use a blockchain scanner to find the source code of the contract at that address, for example the ecsol library as shown on etherscan.io.
Solution 5
If you want to use nodejs to confirm, you can do this:
const Web3 = require('web3')
// make sure you are running geth locally
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
is_contract = async function(address) {
res = await web3.eth.getCode(address)
return res.length > 5
}
is_contract('your address').then(console.log)
Related videos on Youtube
bortzmeyer
I work at Afnic, the domain name registry for .fr. So, I know a bit about DNS.
Updated on July 21, 2022Comments
-
bortzmeyer almost 2 years
An address in Solidity can be an account or a contract (or other things, such as a transaction). When I have a variable x, holding an address, how can I test if it is a contract or not?
(Yes, I've read the chapter on types in the doc)
-
manidos about 7 yearsHere's some info on how this function works
-
eth over 5 yearsThis code is dangerous and no longer advisable as it is hackable because EXTCODESIZE returns 0 in a contract's constructor.
-
Malone over 3 yearsThis link is clearly outdated. It's 2021, this answer is from 2016.
-
James Moore about 3 yearsThe gas consumed by sending to contract is utterly different than the gas consumed by sending to an address. If there were a goal to treat those two things in the same way, there couldn't be a gas distinction.
-
Luke Hutchison over 2 yearsIt should probably be pointed out that
require(msg.sender == tx.origin)
only detects if the caller of a function is an EOA, it can't be used to detect if any other third-party contract is an EOA (such as a contract that you want to call from your own function). -
eth over 2 years@LukeHutchison Upvoted, great point! Added the caller and callee case; happy to hear from you if I missed some things or you have other suggestions.
-
Luke Hutchison over 2 yearsFor completeness you could add that extcodesize is now abstracted away to
<address>.code.size
in solidity (no assembly needed). People need to recognize this form too. (I might have the syntax wrong, I'm not near a computer right now.) -
Matthew Scerri about 2 yearsI don't think it makes much of a difference, but I'm curious as to why you went with
return res.length > 5
? If it's not a Smart contract shouldn'tres
be0x
, meaningres.length > 2
should work just as well? I guess you can also test forres.startsWith("0x6080604052")
?