Other answers return false positives for contracts with no fallback function defined. Assembly code seems like the only way to go.. combined Tjaden Hess's answer with How do I construct a call to another contract using inline assembly?
Answer:
function testIsSafe(address _addr)
private
returns (bool _isSafe)
{
bytes32 sig = bytes4(keccak256("isSafe()"));
bool _success = false;
assembly {
let x := mload(0x40) // get free memory
mstore(x, sig) // store signature into it
_success := call(
5000, // 5k gas
_addr, // to _addr
0, // 0 value
x, // input is x
4, // input length is 4
x, // store output to x
32 // 1 output, 32 bytes
)
// _isSafe is: _success && output
_isSafe := and(_success, mload(x))
}
}
This tests various contracts against the above:
pragma solidity ^0.4.19;
contract Safe {
function isSafe() public pure returns (bool) { return true; }
}
contract FakeSafe {
function isSafe() public pure returns (uint) { return 12345; }
}
contract NotSafe {
function isSafe() public pure returns (bool) { return false; }
}
contract NoFallback {
// function() public {}
}
contract EmptyFallback{
function() public {}
}
contract Caller {
Safe safeInstance;
NotSafe notSafeInstance;
FakeSafe fakeSafeInstance;
NoFallback noFallbackInstance;
EmptyFallback emptyFallbackInstance;
function Caller() public {
safeInstance = new Safe();
fakeSafeInstance = new FakeSafe();
notSafeInstance = new NotSafe();
noFallbackInstance = new NoFallback();
emptyFallbackInstance = new EmptyFallback();
}
// returns true
function testSafe()
public
returns (bool _isSafe)
{
return testIsSafe(address(safeInstance));
}
// returns false
function testNotSafe()
public
returns (bool _isSafe)
{
return testIsSafe(address(notSafeInstance));
}
// returns true
function testFakeSafe()
public
returns (bool _isSafe)
{
return testIsSafe(address(fakeSafeInstance));
}
// returns false
function testNoFallback()
public
returns (bool _isSafe)
{
return testIsSafe(address(noFallbackInstance));
}
// returns false
function testEmptyFallback()
public
returns (bool _isSafe)
{
return testIsSafe(address(emptyFallbackInstance));
}
function testIsSafe(address _addr)
private
returns (bool _isSafe)
{
bytes32 sig = bytes4(keccak256("isSafe()"));
bool _success = false;
assembly {
let x := mload(0x40) // get free memory
mstore(x, sig) // store signature into it
_success := call(
5000, // 5k gas
_addr, // to _addr
0, // 0 value
x, // input is x
4, // input length is 4
x, // store output to x
32 // 1 output, 32 bytes
)
// _isSafe is: _success && output
_isSafe := and(_success, mload(x))
}
}
}