Solidity 中的地址可以是账户或合约(或其他事物,例如交易)。当我有一个变量 x,持有一个地址时,我如何测试它是否是合约?
(是的,我已经阅读了文档中关于类型的章节)
Solidity 中的地址可以是账户或合约(或其他事物,例如交易)。当我有一个变量 x,持有一个地址时,我如何测试它是否是合约?
(是的,我已经阅读了文档中关于类型的章节)
是的,你可以,通过使用一些 EVM 汇编代码来获取地址的代码大小:
function isContract(address addr) returns (bool) {
uint size;
assembly { size := extcodesize(addr) }
return size > 0;
}
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 还是可以部署合约的地址。)
这不是您可以使用 Solidity 从合约中查询的内容,但如果您只是想知道地址是否包含合约代码,您可以使用您的 geth 控制台或类似的控制台进行检查,例如:
> eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f")
使用十六进制字符串(此处0xbfb2e296d9cf3e593e79981235aed29ab9984c0f
)作为您要查询的地址。这将返回存储在该地址的字节码。
编辑:自从首次编写此答案以来,Solidity 发生了变化,@manuel-aráoz 有正确的答案。
没有办法检查地址是否是合约。以太坊的目标之一是让人类和智能合约都得到平等对待。这导致了智能合约与人类和其他合约无缝交互的未来。它将来可能会改变,但现在任意地址是不明确的。
如果你想使用 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)
如果您手头有信息,您可以做什么。如果交易发件人地址为空或未被占用,那么您可以判断该地址是合约账户还是 EOA(外部拥有的账户)。即在网络上发送创建合约交易时,交易中的接收地址为空/未使用。
来自 github 的参考: https ://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions
希望这可以帮助。
简短的回答:
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,因为 .