21

In other words, is using transfer() safe?

function transfer(address contractB) public
{
    contractB.transfer(1000);
    //balances[msg.sender] -= 1000;
}

How about using call.gas?

function transfer2(address receiver) public
{
    receiver.call.gas(20000).value(1000)();
}

2 Answers 2

23

transfer() and send() should be avoided (because they take a hard dependency on gas costs by forwarding a fixed amount of gas: 2300).

Gas specific code (call{gas: ..., value: ...}("")) should also be avoided.

call{value: ...}("") should be used, for example: contractB.call{value: 1000}("")

It is also critical that you make sure to guard against reentrancy by making all state changes, before call{value: ...}("").

https://diligence.consensys.net/blog/2019/09/stop-using-soliditys-transfer-now explains more about the best practice change.

Any gas specific code should be avoided because gas costs can and will change.

For example, the gas cost for SLOAD was raised from 50 to 200 in 2016, and again in EIP 1884, with some impacts described in: https://chainsecurity.com/istanbul-hardfork-eips-increasing-gas-costs-and-more/


UNGAS is an idea to remove the GAS opcode and smart contracts would not have any notion of gas. (Gas would still be in the protocol, but not in the EVM.)

7
  • 1
    Rather than "prevent", it's better to say "guard against". You can't prevent reentrancy by making state changes before ether transfer. In your example, for example, you can re-enter multiple times, draining the balance of msg.sender. The example from the consensus blog sends the entire balance and has a line that sets the balance to 0 before the send, which guards appropriately in that context against reentrancy. Jan 15, 2020 at 19:36
  • 1
    @Chan-HoSuh Thanks for your comments and edits have been made. (The example in this answer was basically copy-pasted from the question without much thought, so it has now been removed because it was a little nonsensical.)
    – eth
    Jan 18, 2020 at 7:56
  • 1
    @axwell I added a bit more explanation to answer but the linked blog post has more space for it.
    – eth
    Sep 12, 2021 at 5:27
  • 1
    Why is empty string ("") passed in call{value: ...}("")? Nov 28, 2021 at 16:01
  • 1
    @ToniSučić The parameter of call has type bytes memory, and should contain an ABI-encoded call to a function, complete with ABI-encoded concrete parameters. Passing a string into a bytes parameter treats the whole string as a byte array. Both bytes and string start with a uint256 length field, and are then followed by the bytes or characters. In this case "" is being used as an empty byte array (equivalent to a zero-length string). In ABI encoding, an empty array is interpreted to mean "call the fallback function" since it has no function name and no parameters. Jun 1, 2022 at 8:47
7

the above answer is perfect, just notice the change in syntax in the latest version of Solidity.

someAddress.call{value: 1 ether}('');

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.