5

What's the difference between these two forms of calling methods on external contracts?

contract A {
    function foo() {
    }
}

contract B {

  function createFoo (address _contractAddress){
    Contract A = A(_contractAddress);
    A.foo();
  }

  function callFoo (address _contractAddress) {
    address newAddress = _contractAddress;
    newAddress.call(bytes4(sha3("foo()")));
  }
}

createFoo and callFoo seem to do the same thing. How are they different?

3
  • 1
    On the EVM level, they are the same - they are both CALLs. On Solidity level, they are different when it comes to error propagation Commented Nov 12, 2017 at 14:10
  • 2
    @Michael O'Rourke a little offtopic here: when I try to compile that code I receive DeclarationError: Identifier not found or not unique. Contract A = A(_contractAddress); btw. I have not seen such declaration anywhere. I may miss something.
    – iku
    Commented May 19, 2018 at 9:31
  • inside createFoo, it should really be: A a = A(_contractAddress); a.foo(); Commented Mar 25, 2022 at 2:42

2 Answers 2

9

There's a huge difference between them

function createFoo (address _contractAddress){
    Contract A = A(_contractAddress);
    A.foo();
}

If foo() fails the call to createFoo() will also fail and it will propagate reversing any change you have made.

Another difference is you can retrieve the value returned by foo(). If the function returned an address you can assign to a variable.

address who = A.foo();

In the second case if foo() fails, call will return false. But it will not reverse changes you have made, you have to explicitely call revert().

function callFoo (address _contractAddress) {
    address newAddress = _contractAddress;
    newAddress.call(bytes4(sha3("foo()")));
}

And you do not have access to the value returned by foo(), call only return true if it executed successfully or false if it has failed.

2
  • 1
    And what if _contractAddress == this? Will this be external call in the second case?
    – k06a
    Commented Feb 15, 2018 at 20:25
  • 1
    @k06a Yes, if you use .call() it always be an external call.
    – Ismael
    Commented Feb 16, 2018 at 3:43
6

On EVM level, they are the same. They are both CALLs (vs DELEGATECALL or CALLCODE), which creates an internal transaction when the function createFoo or callFoo is called.

On Solidity level, they are different. Especially when it comes to error handling (invalid OPCODE jumping)

A.foo() calls the function and you also get the return value. It also propagates errors. This obviously require you to know the contract ABI beforehand. This is the safest way to call another contract.

aAddress.call(bytes4(sha3("foo()"))) is similar, except it does not require the ABI to be known by your contract. This however does not propagate errors, unless remaining gas becomes zero. You can force it to propagate errors by forwarding all your remaining gas to the call, however you may introduce re-entrancy.

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.