1

I wrote a very simple smart contract like this:

contract Test{
    function test1(address payable addr, uint256 value) payable public {
        addr.transfer(value);
    }
}

And I compiled it using solc 0.5.7 on Remix. Some of the opcodes related to transfer function are like this:

Opcodes

PUSH 8FC            addr.transfer(value)
DUP3            value
SWAP1           addr.transfer(value)
DUP2            addr.transfer(value)
ISZERO          addr.transfer(value)
MUL             addr.transfer(value)

In this part it is very obvious that 8FC, which is the gas stipend (2300) of transfer and send function in solidity, is lost when it is multiplied by zero. As a result, in debug view of Remix, I see this just before executing the CALL opcode in the stack:

Debug View

According to the Yellow Paper, the first parameter of CALL is gas right? Why here the first parameter of CALL is zero and how solidity ensures that transfer and send only forward 2300 gas?

I found another similar question here, but I still couldn't get the answer from their discussion. Any ideas?

1 Answer 1

4

Below calculations are valid as of 09 December 2019 on Ethereum mainnet.

My guess is that most of the confusion comes from the fact that constant of 2300 is used both by Solidity compiler and EVM itself. Here is what I've found so far (assuming that target account is an already created, and not self destructed, contract).

EVM:

During the CALL(gas, ..., value, ...) execution if value > 0 then make available min(gas, gasleft - 9700) + 2300 for the target execution and consume 9000 + 700. So if gas is 0, 2300 will be supplied. If gasleft is 9700, 2300 will be supplied no matter the the gas param. Now after the execution, whatever gas that was not used (even from those 2300) will be returned to the calling context and can be subsequently used. This reveals that value transfer (after all the parameters for the call put on stack), will cost atleast 7400 gas.

If value == 0 then make available min(gas, gasleft - 700) for the target execution and consume 700.

Solidity:

During the address.transfer(value) or address.send(value) compilation if value > 0 then make a CALL(0, ..., value, ...), which per EVM logic will result in 2300 gas available for the target.

But if value == 0 then, Solidity's own stipend logic kicking in, make a CALL(2300, ...), which per EVM logic will result in min(2300, gasleft - 700) gas available for the target.

Now answering your questions directly:

According to the Yellow Paper, the first parameter of CALL is gas right? -> Yes.

Why here the first parameter of CALL is zero and how solidity ensures that transfer and send only forward 2300 gas? -> Solidity knows that if value of the transfer greater than 0, then EVM will add 2300 gas by itself. While if the value of the transfer is 0, then EVM will not add anything, and Solidity tries to provide 2300 by itself.

Though developer should always remember that if value is transferred then 2300 available or it is Out of Gas. While if there is no value transfer, then there are no guarantees at all.

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.