3

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?

  • 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 – libertylocked Nov 12 '17 at 14:10
  • 1
    @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 May 19 '18 at 9:31
3

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.

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

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, privacy policy and cookie policy

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