Solidity

call() 函式如何可靠地工作?

  • November 8, 2022

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的回答轉移代幣?更多解釋)

引用自:https://ethereum.stackexchange.com/questions/138975