EEVM.Gas.Dynamic (eevm v0.1.0)

Copy Markdown View Source

Dynamic gas costs that depend on opcode operands at runtime.

Unlike static costs (which are fixed per opcode), dynamic costs vary based on the actual values being operated on — the size of data being hashed, the number of log topics, the storage slot transition, etc.

Covered Opcodes

OpcodeDynamic CostReference
EXP50 × byte_size(exponent); 10 before Spurious DragonEIP-160
KECCAK2566 × word_count(size)Yellow Paper
CALLDATACOPY, CODECOPY, RETURNDATACOPY3 × word_count(size)Yellow Paper
LOG0–LOG4375 × topics + 8 × data_size (the 375 base is static)Yellow Paper
CREATE26 × word_count(initcode_size)EIP-1014
CREATE/CREATE2 deploy200 × code_sizeYellow Paper
CALL value transfer9,000 gasYellow Paper
CALL, SELFDESTRUCT new account creation25,000 gasYellow Paper, EIP-150, EIP-161
CALL gas forwardingmin(requested, available - available/64); requested before Tangerine WhistleEIP-150
CALL value stipend2,300 gasYellow Paper
SSTOREper-fork schedule, see sstore_cost/4EIP-2200, EIP-2929, EIP-3529

References

Summary

Functions

Gas a CALL-family or CREATE opcode hands to its child frame. From Tangerine Whistle (EIP-150) the caller keeps 1/64 of what it has left and the request is capped at the rest; before, the full request is forwarded, and a request above the available gas is an out-of-gas error in the caller.

25,000 when a CALL brings an account into existence. From Spurious Dragon (EIP-161) that is a value transfer to an empty account; before, a call to any account that does not exist.

Dynamic part of LOG0–LOG4: 375 per topic plus 8 per data byte. The 375 base (G_log) is the opcode's static cost in EEVM.Gas.Static.

25,000 when SELFDESTRUCT brings its beneficiary into existence: from Tangerine Whistle (EIP-150) when the beneficiary does not exist, and from Spurious Dragon (EIP-161) only when a non-zero balance moves to an empty account. Nothing before Tangerine Whistle.

Refund for a SELFDESTRUCT: 24,000 before London when the account has not already been destroyed in this transaction (destroyed?), 0 from London (EIP-3529).

Returns {gas, refund_delta} for an SSTORE writing new_value over current, where original is the slot's value at the start of the transaction. The cold-slot charge (EIP-2929) is separate, in EEVM.Gas.Access.sstore_access_cost/3.

Functions

call_forwarded_gas(available_gas, requested_gas, hardfork)

@spec call_forwarded_gas(
  non_neg_integer(),
  non_neg_integer(),
  EEVM.HardforkConfig.t()
) ::
  non_neg_integer()

Gas a CALL-family or CREATE opcode hands to its child frame. From Tangerine Whistle (EIP-150) the caller keeps 1/64 of what it has left and the request is capped at the rest; before, the full request is forwarded, and a request above the available gas is an out-of-gas error in the caller.

call_new_account_cost(exists?, empty?, value, hardfork)

@spec call_new_account_cost(
  boolean(),
  boolean(),
  non_neg_integer(),
  EEVM.HardforkConfig.t()
) ::
  non_neg_integer()

25,000 when a CALL brings an account into existence. From Spurious Dragon (EIP-161) that is a value transfer to an empty account; before, a call to any account that does not exist.

call_stipend(arg1)

@spec call_stipend(non_neg_integer()) :: non_neg_integer()

call_value_cost(arg1)

@spec call_value_cost(non_neg_integer()) :: non_neg_integer()

code_deposit_cost(code_size)

@spec code_deposit_cost(non_neg_integer()) :: non_neg_integer()

copy_cost(size)

@spec copy_cost(non_neg_integer()) :: non_neg_integer()

create2_hash_cost(init_code_size)

@spec create2_hash_cost(non_neg_integer()) :: non_neg_integer()

exp_dynamic_cost(exponent, hardfork)

@spec exp_dynamic_cost(non_neg_integer(), EEVM.HardforkConfig.t()) ::
  non_neg_integer()

keccak256_dynamic_cost(size)

@spec keccak256_dynamic_cost(non_neg_integer()) :: non_neg_integer()

log_cost(topic_count, data_size)

@spec log_cost(non_neg_integer(), non_neg_integer()) :: non_neg_integer()

Dynamic part of LOG0–LOG4: 375 per topic plus 8 per data byte. The 375 base (G_log) is the opcode's static cost in EEVM.Gas.Static.

selfdestruct_new_account_cost(exists?, empty?, balance, hardfork)

@spec selfdestruct_new_account_cost(
  boolean(),
  boolean(),
  non_neg_integer(),
  EEVM.HardforkConfig.t()
) :: non_neg_integer()

25,000 when SELFDESTRUCT brings its beneficiary into existence: from Tangerine Whistle (EIP-150) when the beneficiary does not exist, and from Spurious Dragon (EIP-161) only when a non-zero balance moves to an empty account. Nothing before Tangerine Whistle.

selfdestruct_refund(destroyed?, hardfork)

@spec selfdestruct_refund(boolean(), EEVM.HardforkConfig.t()) :: non_neg_integer()

Refund for a SELFDESTRUCT: 24,000 before London when the account has not already been destroyed in this transaction (destroyed?), 0 from London (EIP-3529).

sstore_cost(original, current, new_value, hardfork)

Returns {gas, refund_delta} for an SSTORE writing new_value over current, where original is the slot's value at the start of the transaction. The cold-slot charge (EIP-2929) is separate, in EEVM.Gas.Access.sstore_access_cost/3.

  • Before Istanbul: 20,000 to set a zero slot, 5,000 for any other write, and a 15,000 refund for clearing one.
  • Istanbul (EIP-2200) net metering: writes that leave the slot unchanged or touch an already-dirty slot cost SLOAD_GAS (800); refunds track whether the write clears the slot or restores its original value.
  • Berlin (EIP-2929): SLOAD_GAS becomes 100 and the 5,000 reset cost 2,900.
  • London (EIP-3529): the clearing refund drops to 4,800.

Examples

iex> EEVM.Gas.Dynamic.sstore_cost(7, 7, 0, EEVM.HardforkConfig.new(:byzantium))
{5000, 15000}

iex> EEVM.Gas.Dynamic.sstore_cost(7, 7, 0, EEVM.HardforkConfig.new(:istanbul))
{5000, 15000}

iex> EEVM.Gas.Dynamic.sstore_cost(7, 9, 9, EEVM.HardforkConfig.new(:istanbul))
{800, 0}

iex> EEVM.Gas.Dynamic.sstore_cost(7, 7, 0, EEVM.HardforkConfig.new(:london))
{2900, 4800}