Option two – using low-level calls

When the called contract doesn't adhere to the ABI, we can't just use ContractAInstance.f(); or ContractA ContractAInstance = ContractA(0x123456) to define a new instance.

In this case, we have to use the special low-level call function, using the following call structure:

contract_address.call(bytes4(sha3("function_name(arguments types)")), parameters_values)

In the previous example, we could call f() with two arguments using ContractAaddress.call(bytes4(keccak256("f(uint256,string)")), 10, ”hello”);.

This is a sort of tedious manual construction of the ABI function signature. However, things are getting better with newer Solidity. Since release 0.4.22, the abi.encode(), abi.encodePacked(), abi.encodeWithSelector(), and abi.encodeWithSignature() global functions have been defined to encode structured data, therefore helping us to build a valid call, as follows:

OneInstance.call(abi.encodeWithSignature("function_name(arguments types)")),parameters_values)

call can be used along with the .gas() and value() methods to adjust the supplied gas and value in the call.

call returns true if the called function executes without exception. Also, it will fire an exception if the call contract does not exist, or if it throws an exception or runs out of gas. If we apply call to a contract without specifying a function name, its fallback will be executed (maybe that's why the fallback function doesn’t have a name).

Let's clear up some potential ambiguity about the call function. The call function used in Solidity to pass messages (the CALL opcode) between contracts is different from the call method (based on the eth_call RPC message) we introduced in the previous chapter, which is part of the web3.js API. The main difference to keep in mind is that the former can change a contract’s states while the latter can’t.