26

Solidity 中的地址可以是账户或合约(或其他事物,例如交易)。当我有一个变量 x,持有一个地址时,我如何测试它是否是合约?

(是的,我已经阅读了文档中关于类型的章节)

4

7 回答 7

55

是的,你可以,通过使用一些 EVM 汇编代码来获取地址的代码大小:

function isContract(address addr) returns (bool) {
  uint size;
  assembly { size := extcodesize(addr) }
  return size > 0;
}
于 2016-12-02T19:08:51.240 回答
18

isContract使用 EXTCODESIZE的函数获得最高票数的答案被发现是可破解的。

如果从合约的构造函数调用该函数,则该函数将返回 false(因为该合约尚未部署)。

如果有的话,应该非常小心地使用代码,以避免安全黑客攻击,例如:

https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide存档

重复:_

不要使用 EXTCODESIZE 检查来阻止智能合约调用函数。这不是万无一失的,它可以被构造函数调用破坏,因为在构造函数运行时,该地址的 EXTCODESIZE 返回 0。

请参阅欺骗 EXTCODESIZE 返回 0 的合约的示例代码。


检查调用者是否是合约

如果您想确保 EOA 正在调用您的合约,一个简单的方法是require(msg.sender == tx.origin). 但是,阻止合同是一种具有安全性互操作性考虑的反模式。

require(msg.sender == tx.origin)实施帐户抽象时将需要重新访问。

检查被调用者是否是合约

正如@Luke 在评论中指出的那样,没有通用的链上方法来了解被调用者。如果你想“调用”一个地址,没有通用的方法来确定该地址是合约、EOA 还是可以部署新合约的地址,或者它是否是 CREATE2 地址。

一种适用于某些被调用者的非通用方式:您可以在链上进行映射,以存储已知 EOA 或合约的地址。(请记住,对于没有任何链上历史记录的地址,您无法知道它是 EOA 还是可以部署合约的地址。)

于 2019-01-05T22:26:16.080 回答
11

这不是您可以使用 Solidity 从合约中查询的内容,但如果您只是想知道地址是否包含合约代码,您可以使用您的 geth 控制台或类似的控制台进行检查,例如:

  > eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f")

使用十六进制字符串(此处0xbfb2e296d9cf3e593e79981235aed29ab9984c0f)作为您要查询的地址。这将返回存储在该地址的字节码。

您还可以使用区块链扫描仪在该地址查找合约的源代码,例如etherscan.io上显示的ecsol

于 2016-11-24T15:42:22.343 回答
8

编辑:自从首次编写此答案以来,Solidity 发生了变化,@manuel-aráoz 有正确的答案。

没有办法检查地址是否是合约。以太坊的目标之一是让人类和智能合约都得到平等对待。这导致了智能合约与人类和其他合约无缝交互的未来。它将来可能会改变,但现在任意地址是不明确的。

于 2016-06-07T04:27:50.933 回答
0

如果你想使用 nodejs 来确认你可以这样做:

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)
于 2022-02-15T06:40:00.937 回答
-2

如果您手头有信息,您可以做什么。如果交易发件人地址为空或未被占用,那么您可以判断该地址是合约账户还是 EOA(外部拥有的账户)。即在网络上发送创建合约交易时,交易中的接收地址为空/未使用。

来自 github 的参考: https ://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions

希望这可以帮助。

于 2016-06-28T21:06:48.540 回答
-3

简短的回答:

require(tx.origin == msg.sender);

tx.origin 是对发起这个串行函数调用的原始地址的引用,而 msg.sender 是直接调用目标函数的地址。这意味着,tx.origin 必须是人类,msg.sender 可以是合约或人类。因此,如果有人通过合约给你打电话,那么 msg.sender 是一个不同于 tx.origin 的合约地址。

我知道大多数合同可能会使用@Manuel Aráoz 的代码,该代码在大多数情况下都有效。但是如果你在合约的构造函数中调用一个函数,extcodesize 将返回 0,这会导致 isContract 检查失败。

注意:如果您不清楚它代表什么,请不要在其他情况下使用 tx.origin,因为 .

于 2018-10-26T06:47:07.740 回答