6

Can anyone tell me why sometimes you have to us the .call method and abi.encodeWithSignature when you want to call a function on an an external contract (First example below) and other times you can simply call the contract name.function with the parameters (Second example below).

Aside from the obvious difference than one is in the constructor and one is in a function I am assuming the 2nd method of calling does not have to use .call because we have imported the contract but if anyone can clarify when to use the different methods would be appreciated?

Contract using the .call method...

pragma solidity 0.8.13;

contract callerContract{
    address payable targetAddress = payable(0x3328358128832A260C76A4141e19E2A943CD4B6D);
     bytes public result;

     constructor() payable{}
    
    function callExternal(uint256 _number) public{
        (bool success, bytes memory returnData) = targetAddress.call{value: 100, gas: 10000}(abi.encodeWithSignature("targetFunction(uint256)",_number));
        result = returnData;
    }
}

Contract that calls function with contract name.functionName with value and function parameters (No .call method and no abiEncodeWithSignature to access function needed here) I am referring to gateway.depositETH... etc..

pragma solidity ^0.7.5;

import "./IERC20.sol";
import "./IWETHGateway.sol";

contract Escrow {
    address arbiter;
    address depositor;
    address beneficiary; 
    uint initialDeposit;
    
    
    IWETHGateway gateway = IWETHGateway(0xDcD33426BA191383f1c9B431A342498fdac73488);
    IERC20 aWETH = IERC20(0x030bA81f1c18d280636F32af80b9AAd02Cf0854e);

    constructor(address _arbiter, address _beneficiary) payable {
        arbiter = _arbiter;
        beneficiary = _beneficiary;
        depositor = msg.sender;
        initialDeposit = msg.value;

        gateway.depositETH{value: address(this).balance}(address(this), 0);
        
    }

2 Answers 2

5

The first method is called "low-level call". It's generally used to save gas on the call or to handle a revert.

Example:

// this is a low level call
(bool success, bytes memory returndata) = token.call(abi.encodeWithSignature(IERC20.transfer.selector, to, value));

// this is a normal call
IERC20(token).transfer(to, value);

// this is a how a normal call works using low-level call
require(token.code.size > 0);
(bool success, bytes memory returndata) = token.call(abi.encodeWithSignature(IERC20.transfer.selector, to, value));
require(success);  // ... also forwards the error message

Basically a normal call adds automatically a check that the contract exists, and that the call was successful. Using a low-level call you can skip these checks to save gas.

2
  • Thanks, so as the other poster mentioned, if you have the interface or import the contract you are calling into your own contract than you can do the normal call ... IERC20(token).transfer(to, value); Otherwise if you don't import the contract or have the interface to work with than you must do the low level call with abi.encode? Is that correct?
    – Blockpain
    Sep 21, 2022 at 23:53
  • Yes, that's correct. But generally since importing an interface is really simple, when you see the low-level calls it's because the developers wanted to save gas.
    – 0xSanson
    Sep 22, 2022 at 10:07
0

when using call, you need to specify the target function on the contract that is called. Hence, encoding the function signature with (abi.encodeWithSignature("targetFunction(uint256)" followed by the values passed as arguments ( in this case a "uint", _number). The encoding specifies the function getting called by its function signature. you can get more here

3
  • Thanks for the response. I understand how to use it, but i don't understand why sometimes you call external functions with this .call & abi method and other times you can simply call it the way the 2nd contract in my example calls it. Aside from the fact that its in the constructor in the 2nd contract, why aren't we using the .call method and abi.encode here? Instead we are simply are calling the contractName.function with a value and the parameters. No .call or abi.encode needed in this example. When do you use each of the different methods?
    – Blockpain
    Sep 21, 2022 at 11:25
  • the first method is because you only instantiate the contract's address without its interface, so you need to add extra information of the target function information. In the second sample, you initialized the contract with its interface which means that all the external methods are defined. hence, you you don't have to add extra information for the function call as it is already defined on the interface.
    – phydy
    Sep 21, 2022 at 11:32
  • Okay this is what i suspected. Thank you for the clarification. Appreciate it
    – Blockpain
    Sep 21, 2022 at 11:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.