為什麼 SafeERC20 假設代幣的 transfer 和 transferFrom 返回值是可選的?
在 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 標準中定義的內容,也是本契約引用的IERC20 介面中定義的內容。
快速回答:並不總是有返回值。
長答案:
雖然它不完全符合 ERC20,但有很多 ERC20 代幣不會返回任何東西。相反,它們只是像
BadERC20Token
以下範例中那樣建構:contract FullCompliantERC20Token { function transfer() returns (bool) { return true; } } contract BadERC20Token { function transfer() {} }
假設您
transfer
在 DEX 中對 -function 執行外部函式呼叫,並且您希望該函式返回一個布爾值,那麼如果此函式在該呼叫之前已存在於記憶體中,則此返回值中可能存在一些垃圾沒有顯式返回值。這是可能的,因為函式的返回值不是實際函式選擇器的一部分!因此,transfer()
沒有返回值的函式和函式transfer() returns(bool)
具有相同的函式選擇器,儘管它們仍然不同。它們將以相同的方式執行,但在非返回值版本中,返回值將包含隨機垃圾並導致錯誤。實際上這是自 2017 年拜占庭硬分叉以來的一個大問題。在此之前,返回值是來自函式呼叫本身的常量值,但自從拜占庭以來,返回值有點隨機,會導致錯誤甚至安全問題。
為了真正檢查傳遞函式是否真的有意返回布爾值,唯一的機會是檢查 RETURNDATASIZE,它是由 Byzantinum fork 發明的 EVM 操作碼。
這發生在
SafeERC20
:它只是檢查布爾返回值是否存在。僅當返回值存在時,它才會評估該值並恢復或不恢復。通過這種方式,它為呼叫合約提供了更高的可靠性和安全性。
好吧,如您所見,該函式被呼叫
_callOptionalReturn
。儘管標準規定需要返回值,但某些合約可能會認為 ERC20 代幣就像叢林,你可以做任何你想做的事情,並且:不要在其傳輸函式中返回 bool 值。因此,如果您希望 bool 返回但該函式不返回任何內容,則可能會發生不好的事情,例如訪問您不打算訪問的記憶體。
如需進一步閱讀,您可以查看此處: https ://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca