在openZeppelin的SafeERC20中,函数safeTransfer和safeTransferFrom调用一个名为_callOptionalReturn的内部函数,该函数对令牌的transfer和transferFrom函数进行低级调用,并检查返回值是否为真。
这是代码:
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}为什么要检查返回值?对于ERC20、transfer和transferFrom,不是总是有一个布尔返回值吗?
这是在ERC20 20标准中定义的,也是在这个契约引用的IERC20 20接口中定义的。
发布于 2022-10-07 19:24:38
快速回答:并不总是有一个返回值。
虽然它并不完全符合are 20,但是有很多are 20标记不返回任何东西。相反,在下面的示例中,它们只是像BadERC20Token一样构建:
contract FullCompliantERC20Token {
function transfer() returns (bool) { return true; }
}
contract BadERC20Token {
function transfer() {}
}假设您在DEX中执行对transfer-function的外部函数调用,并且您希望该函数返回一个布尔值,那么如果该函数没有显式返回值,则该返回值中可能有一些垃圾存在于该调用之前的内存中。这是可能的,因为函数的返回值不是实际函数选择器的一部分!因此,没有返回值的函数transfer()和函数transfer() returns(bool)具有相同的函数选择器,尽管它们仍然不同。它们将以同样的方式执行,但在不返回值的版本中,返回值将包含随机垃圾并导致错误。
实际上,这是一个大问题,因为拜占庭硬叉子在2017年。在此之前,返回值是函数调用本身的一个常量值,但由于Byzantinum返回值是随机的,因此会导致错误,甚至安全问题。
为了真正检查传递函数是否真的有意返回布尔值,唯一的机会是检查RETURNDATASIZE,这是一种EVM操作码,它是用Byzantinum叉发明的。
这是在SafeERC20中发生的:它只是检查,是否存在布尔返回值。只有当返回值存在时,它才会计算这个值,或者恢复或不恢复。这样可以使呼叫合同更可靠、更安全。
发布于 2022-10-07 19:21:40
如您所见,这个函数名为_callOptionalReturn。尽管标准指定需要返回值,但一些契约可能认为ERC20令牌就像丛林一样,您可以随心所欲地做任何事情,并且:不要在它们的传递函数中返回bool值。
因此,如果您期望一个bool返回,但是该函数没有返回任何内容,那么可能会发生一些糟糕的事情,比如访问您不打算访问的内存。
要进一步阅读,您可以在这里查看:https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
https://ethereum.stackexchange.com/questions/137049
复制相似问题