If I make a call to an external smart contract that uses a function (transfer) which of the following is safer.

  1. using abi.encodeWithSignature
(bool success, ) = address(contractInstance).call(abi.encodeWithSignature("transfer(address, uint256)", msg.sender, amount));
require(success, "Failed to send tokens");
  1. direct call
contractInstance.transfer(msg.sender, amount)

for both im using reentrancy guard.


  • 1
    the only safe way is to check the balance after the transfer, it must be equal to balanceBefore + amountTransferred , everything else is unsafe. A token can report transfer as successful via event but don't really transfer anything. you have to add balanceOf() call (and spend more gas). Check out , for example, the flash() function of UniswapV3Pool contract, this is 100% safe
    – Nulik
    Commented Feb 1, 2023 at 18:15
  • That’s interesting. Definitely will check. Thanks
    – kapitankot
    Commented Feb 1, 2023 at 18:21
  • 1
    Are the two codes the same think? I am confused, looks that the first is calling "transfer" function as member of address type, and the second is calling "transfer" as a function of the contract with possible arbitrary implementation. Anyway, the documentation says to avoid ".call()", so i recomend use the second way.
    – Rafael
    Commented Feb 1, 2023 at 18:34

1 Answer 1


They both do the same thing but the second is suggested cause it's simpler, so less error-prone.

As @Nulik said, when dealing with unknown ERC20 tokens there can be other risks. Check this blog for examples.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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