EIP-5 Improving Gas Usage for RETURN and CALL* (Gas Usage for `RETURN` and `CALL*`)

Final · EVM & execution · EIP status Final · created 2015-11-22

Changes gas charging so that when a contract calls another contract, gas is charged only for the memory actually used at the point the call ends, not for the entire memory reserved in advance for output. This makes it possible to receive back strings and arrays whose length cannot be known in advance.

Why it is needed

In today's Ethereum Virtual Machine (EVM), when a contract calls another contract's function, it must decide the size of the space to receive the result in advance. However, the size of the result is often hard to know before actually making the call, since data in the other contract's storage cannot be seen directly from outside, and finding out its size would require yet another call. One option is to reserve a generously large space, but currently gas must be paid even for memory that goes unused, driving up the cost. As a result, receiving back strings or arrays of variable length is expensive and inconvenient, to the point of being practically unusable. This EIP solves both problems at once by charging gas only for memory that is actually used.

What changes

Today

After EIP-5

Who is affected

In depth

Scope: CALL, CALLCODE, DELEGATECALL

The gas and memory rule changes apply to CALL, CALLCODE, and DELEGATECALL (the source groups these three together as CALL*). CREATE does not write to memory, so it is unaffected. The arguments of CALL* are gas, address, value, input_start, input_size, output_start, and output_size.

Charging Gas at Two Points in Time

At the start of the opcode, gas is charged only to grow memory up to input_start + input_size, and nothing is charged for output_start + output_size. If the called contract returns data of size n, the caller's memory grows up to output_start + min(output_size, n), and that much gas is charged at that point. The output is written into the range [output_start, output_start + min(n, output_size)). As a result, the caller can run out of gas at either the start or the end of the opcode.

MSIZE After the Call and the Proxy Pattern

After the call ends, the MSIZE opcode must return the size by which memory has actually grown. If the caller calls with output_start = MSIZE and output_size = 2**256-1, the region actually used becomes (output_start, MSIZE after the call), revealing the size of the returned data. Thanks to this property, there is no need to know the output size in advance, since it can be determined after the call, which makes it possible to build proxy contracts that pass through the input and output of a contract whose interface is unknown.

Design Alternatives and Rationale for the Chosen Approach

Alternatives considered included changing the opcode itself or the number of arguments, and changing the gas rule only when output_size equals 2**256-1. However, since the main implementation difficulty is having to grow memory at two points around CALL, a conditional approach does not make the implementation any simpler. Early on, there was also a proposal to push the size of the returned data onto the stack, but the MSIZE approach above was judged sufficient and far better for backward compatibility, so the current approach of changing the EVM as little as possible was chosen.

Backward Compatibility: Gas and Memory Size

Because contracts can read their remaining gas and memory size, this change could alter the meaning of existing contracts. On the gas side, however, the risk is low: the VM never charges more gas than before, and the contract can only end up refunding more gas to the caller. On the MSIZE side, there are two effects. First, the memory chunk that CALL was meant to reserve becomes smaller than before, which could overlap with later MSIZE-based allocations, though it is unlikely that such contracts exist. Second, the memory addresses of objects allocated via MSIZE change, but since objects should be written to not depend on absolute or relative position (that is, to be relocatable), this is generally not a problem.

Implementation Notes and Status

VM implementers must wait until the call completes and confirm that enough gas remains before growing memory. A typical usage is reserving 2**256-1 bytes for output. The original text includes links to the Python reference implementation before and after the change. This EIP is a standard finalized in Final status.

Original EIP · ← EIPs