2

I always thought these two calls were equivalent:

Receiver(r).onTokenReceived(a, u);

r.call(bytes4(sha3("onTokenReceived(address,uint256)")), a, u);

It turns out they both work if the receiver has onTokenReceived(a,u), and they both work if the receiver has a fallback function. But if the receiver has no fallback function defined, the first line fails with invalid opcode, but the second line runs without error! Why is this?

Here's my full test code. Only test5 fails.

pragma solidity ^0.4.13;

contract Receiver {
    event Log(address addr, uint val);
    function onTokenReceived(address from, uint val) 
    returns (bool) {
        Log(from, val);
        return true;
    }
}

contract FallbackReceiver {
    event Log(uint x);
    function () {
        Log(uint8(msg.data[0]));
    }
}

contract NonReceiver {}

contract Sender {
    Receiver rec = new Receiver();
    address fallbackRec = address(new FallbackReceiver());
    address nonRec = address(new NonReceiver());
    address a = 0x1;
    uint u = 1;

    function test1() {
        rec.onTokenReceived(a, u);
    }
    function test2() {
        address(rec).call(bytes4(sha3("onTokenReceived(address,uint256)")), a, u);
    }
    function test3() {
        Receiver(fallbackRec).onTokenReceived(a, u);
    } 
    function test4() {
        fallbackRec.call(bytes4(sha3("onTokenReceived(address,uint256)")), a, u);
    } 
    function test5() {
        Receiver(nonRec).onTokenReceived(a, u);
    }
    function test6() {
        nonRec.call(bytes4(sha3("onTokenReceived(address,uint256)")), a, u);
    } 
}
1

Ok I think I've got this figured out.

For one thing, from another answer: "Please note the difference: _target.call(..) returns false if call was not successful, while cm.registrationRequest(..) throws an exception."

Interaction between two smart contracts - 'call' function fails. Why?

Presumably this is because Solidity compiles the static call with code that throws if false is returned.

Also, it turns out that if there's an empty fallback function defined, it returns true by default, but if there's no such fallback it returns false. Test code:

contract F {
    function () {}
}

contract N {}

contract Test {
    event Log(bool x);
    function TestF() {
        address f = address(new F());
        Log(f.call());
    }
    function TestN() {
        address n = address(new N());
        Log(n.call());
    }
}

I.e. it must be that fallbacks return false by default but when Solidity compiles F it actually compiles the fallback to function () { return true; }...which is consistent with how functions generally work in Solidity.

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.