Solidity
call() 函式如何可靠地工作?
Solidity 中的學習
call()
功能,我讀到我們call()
只在不知道被呼叫合約的 ABI 並且不知道其原始碼的情況下使用。下面我有一個例子,但似乎我們確實知道原始碼,因此我們可以呼叫“setX(uint256)”或者它是如何工作的*?*誰能告訴我這種困惑?
function callSetX(address payable _addr, uint256 x) public payable { // call setX() sends eth (bool success, bytes memory data) = _addr.call{value: msg.value}( abi.encodeWithSignature("setX(uint256)", x) ); emit Response(success, data); }
如果您通過函式發送乙太幣,建議使用呼叫函式
fallback
該
call()
功能是允許與其他智能合約互動的低級別,您也可以實現delegatecall()
,這些情況的一個例子是:// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; contract Receiver { event Received(address caller, uint amount, string message); fallback() external payable { emit Received(msg.sender, msg.value, "fallback was called"); } function foo(string memory _message, uint _x) public payable returns (uint) { emit Received(msg.sender, msg.value, _message); return _x + 1; } } contract caller { event Response(bool success, bytes data); // Imagine that contract B doesn't have the source code for contract A // But still we do know the address of A and the function to call function testCallFoo(address payable _addr) public payable { // Here is where we can send ether and also specify gas amount (bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(abi.encodeWithSignatures("foo(string, uint256)", "call foo", 123)); emit Response(success, data); } } // Calling a function that doesn't exist triggers the fallback function function testCallDoesNotExist(address _addr) public { (bool success, bytes memory data) = _addr.call(abi.encodeWithSignatures("doesNotExist()")); emit Response(success, data); }
並且
delegatecall()
是呼叫函式(具有類似行為的低級函式)。當合約 A對合約 B執行 delegatecall 函式時,合約 B的程式碼以 (contract A’s
storage
) 執行,即 (msg.sender & msg.value)。// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; // Deploy this contract first contract B { // Storage layout must be the same as contract A uint public num; address public sender; uint public value; function setVars(uint _num) public payable { num = _num; sender = msg.sender; value = msg.value; } } contract A { uint public num; address public sender; uint public value; function setVars(address _contract, uint _num) public payable { // A storage is set | B is not modified (bool success, bytes memory data) = _contract.delegatecall(abi.encodeWithSignature("setVars(uint256)", _num)); } }
我讀到只有在我們不知道被呼叫合約的 ABI 並且我們不知道它的原始碼時才使用 call()
這不是真的,我想知道你什麼時候讀到的。正如您已經知道的那樣,要使用 .call 呼叫函式,您需要知道它的名稱(或者至少它的選擇器,如果您真的無法訪問原始碼,您可以從反彙編中找出它)以及它需要的參數(如果有的話)。.call 主要用於 Gonzalo 的回答提到的情況(當您不確定收件人是 EOA 時發送 Ether,因為否則推薦的 .transfer() 不會轉發足夠的氣體以允許合約做任何事情),但是有其他一些,例如能夠在通話恢復後繼續進行交易
function callX(address x) { // If the call to x reverts, 'success' will be set to false instead of the whole transaction reverting. (bool success, bytes memory data) = x.call(""); if(success) { doSomething(); } else { doSomethingElse(); } }
我能想到的另一個問題是,當您不確切知道被呼叫函式的行為方式時(Uniswap 的 safeTransfer 就是一個很好的例子,請閱讀我關於為什麼 Uniswap V2 使用
_safeTransfer
的回答轉移代幣?更多解釋)