8

When we call a function on an external contract, the EVM computes a bytes buffer which contains the function signature and arguments.

There are two ways to serialize arguments:

  1. abi.encode
  2. abi.encodePacked

The differences between the two methods are well documented and I have understood them.

What I want to understand is why there are two methods in the first place? It is not clear when we should use the first and when we should use the second.

How can I see which method is used when I make this calls?

Call 1:

(success, ) = address(c).call(abi.encodeWithSignature("myfunction(uint256,uint256)", 400,500));

Call 2:

(success, ) = address(c).call(abi.encodeWithSelector(bytes4(keccak256("myfunction(uint256,uint256)")), 400,500)  )

Call 3:

(success, ) = address(c).call(  bytes4(keccak256("myfunction(uint256,uint256)")),    400, 500);

Call 4:

contract_instance.myfunction(400,500);

Thanks a lot.

7

I'd recommend to use 4 at all times if possible. It is easy to remember and write. The behavior is that it will revert the whole transaction if the call fails, which is the most reasonable alternative in general.

  • abi.encode encode its parameters using the ABI specs. The ABI was designed to make calls to contracts. Parameters are padded to 32 bytes. If you are making calls to a contract you likely have to use abi.encode

  • abi.encodePacked encode its parameters using the minimal space required by the type. Encoding an uint8 it will use 1 byte. It is used when you want to save some space, and not calling a contract.

  • abi.encodeWithSignature same as encode but with the function signature as first parameter. Use when the signature is known and don't want to calculate the selector.

  • abi.encodeWithSelector same as encode but selector is the first parameter. It almost equal to encodeWithSignature, use whatever fits best.

Case 3 was deprecated do not use, call+abi.encode should do the same job.

2
  • Thanks a lot. So you mean abi.encodePacked is not used in my 4 calls example ? abi.encodePacked is used manually when i want to serialize values for another usage than function calls ?
    – Bob5421
    Jan 3 at 19:30
  • @Bob5421 When calling a function directly in solidity the compiler generate code similar to abi.encode. The function abi.encodePacked is mostly used to serialize things, like in signatures messages for example. It can be used for functions calls but you should ensure the data layout generated matches the expected by the function.
    – Ismael
    Jan 3 at 20:52
3

Cas 4 is more expensive and safer than other cases:

Due to the fact that the EVM considers a call to a non-existing contract to always succeed, Solidity includes an extra check using the "extcodesize" opcode when performing external calls. This ensures that the contract that is about to be called either actually exists (it contains code) or an exception is raised.

The low-level calls which operate on addresses rather than contract instances (i.e. .call(), .delegatecall(), .staticcall(), .send() and .transfer()) do not include this check, which makes them cheaper in terms of gas but also less safe.

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.