Skip to content

Instantly share code, notes, and snippets.

@ritzdorf ritzdorf/EIP1884.md Secret
Last active Sep 16, 2019

Embed
What would you like to do?
Recently Executed Transactions affected by EIP1884

Recently Executed Contracts with Issues in EIP 1884

Background

EIP 1884 is set to be implemented into the upcoming Ethereum 'Istanbul' hard fork. It

  • increases the cost of opcode SLOAD from 200 to 800 gas
  • increases the cost of BALANCE and EXTCODEHASH from 400 to 700 gas
  • adds a new opcode SELFBALANCE with cost 5.

What is this?

  • Here we list our analysis on contracts might stop working with EIP-1884.
  • This is not to blame the developers (as they generally followed best practices) but to raise awareness of the issue so developers can redeploy or take other measures.
  • In our analysis we have focused on recently executed contracts, it is therefore not complete
  • Below we list contracts that get called by other contracts with a fixed amount of gas. The most common case for calls with a fixed amount of gas is Solidity's .transfer() function, but Solidity also allows to add .gas(X) to calls to only pass X amount of gas. And there are several other sources. The receiving contracts currently successfully execute but would run out-of-gas due to the higher gas costs of EIP-1884.
  • We currently see roughly 0.15 affected transactions per block. Mostly these transactions are related to the first two contracts listed below.
  • For more analysis see: https://github.com/holiman/eip-1884-security
  • Please contact @HRitzdorf on Telegram or Twitter in case of questions or comments

KyberNetwork

  • Called primarily through internal transactions, > 2,8 million message calls
  • Fallback function:
    function() public payable {
        require(reserveType[msg.sender] != ReserveType.NONE);
        EtherReceival(msg.sender, msg.value);
    }

CappedVault

  • 4,263 ETH, over 70,000 internal transactions
  • Fallback function:
    function () public payable {
        require(total() + msg.value <= limit);
    }
  • Triggers 2 SLOADS and 1 BALANCE
  • Currently has 1342 gas left, would break with EIP 1884

0x5Da156439474d5B27d0AAb0781AF06428068334C

Token with Caller-dependent Fallback

  • 217 transactions, 0.14 ETH
  • Fallback function checks the caller against some storage variable, in case of a mismatch it performs a delegatecall based on another storage location Source, Example TX
  • Currently, has 820 gas left in fallback function, would break with EIP 1884.
  • A second instance of this contract, Eternity Token, 250 transactions
  • A third instance of this contract, Forge Token, Example TX
  • A fourth instance, Dominion Token, Example TX

RelayHub

  • Recently deployed, August 12th, 200 Transactions since
    // Gas stipends for acceptRelayedCall, preRelayedCall and postRelayedCall
    uint256 constant private acceptRelayedCallMaxGas = 50000;
    uint256 constant private preRelayedCallMaxGas = 100000;
    uint256 constant private postRelayedCallMaxGas = 100000;
  • Example Issue: Used to call 0x557f91c8ea60aaf2c33b9be3a80ac691103515f4, currently consumes 33538 of 50000 gas, but performs 96 SLOADs and hence would break in the future, Example TX

Forwarded Delegatecall

Aragon's DepositableDelegateProxy

  • Deployed once per Aragon organisation (rougly 600)
  • Relevant Code:
    function isDepositable() public view returns (bool) {
        return DEPOSITABLE_POSITION.getStorageBool();
    }

    event ProxyDeposit(address sender, uint256 value);

    function () external payable {
        // send / transfer
        if (gasleft() < FWD_GAS_LIMIT) {
            require(msg.value > 0 && msg.data.length == 0);
            require(isDepositable());
            emit ProxyDeposit(msg.sender, msg.value);
        } else { // all calls except for send or transfer
            address target = implementation();
            delegatedFwd(target, msg.data);
        }
    }
}
  • The current execution of the fallback function in case of a send/transfer consumes 1759 gas. It contains one SLOAD due to isDepositable(). Therefore, it will break with EIP-1884.
  • Example TX
  • Issue at Aragon

Contract calling TokenStore

  • This contract calls the trade function of the very popular TokenStore contract with 100,000 gas - Source
  • Contract had over 2,000 transactions at time of writing
  • For certain trades the 100,000 gas will still be sufficient after the hard fork, but for certain trades the 100,000 gas won't be sufficient where they currently are
  • Example transaction that would fail in the future:
    • Action 6 in parity trace: 100,000 gas provided, 95,728 gas used, 13 SLOADs, hence will break
    • Action 8 in parity trace: 95,728 gas provided (all the remaining gas), 95,728 gas used, 13 SLOADs, hence will break even if 100,000 gas would be provided
  • This is an example where the sender contract should be redeployed

MonsterBit

  • MonsterBit is a collectible game on Ethereum
  • The affected contract had been the entry point of over 36,000 transactions at the time of writing
  • Problematic fallback function can have different number of SLOADs: (Source)
def _fallback() payable: # default function
  if saleAuctionAddress != caller:
      if siringAuctionAddress != caller:
          if unknowne4c73abeAddress != caller:
              require caller == unknowna6531d34Address
  • Any invocation with three or more SLOADs will break. Current cost is 964 gas in case of three SLOADs.
  • Example TX: One call has 3 and another has 4 SLOADs

PicoStocksWallet

  • 488 transactions and 11.8 ETH
  • Problematic fallback function only emits a single event: (Source)
def _fallback() payable: # default function
  log Transfer(
        address from=call.value,
        address to=caller,
        uint256 value=owner)
  • Performs single SLOAD for owner
  • Current cost 2135 gas, would break after hard fork
  • Example TX

PickFlix Games

  • PickFlix is a Weekly Fantasy Movie Game
  • Affected Contracts have an expensive event inside their fallback function: (Source)

def _fallback() payable: # default function
  log Received(
        address user=call.value,
        uint256 ethers=eth.balance(this.address),
        uint256 tokens=caller)
  • It is an example where the gas cost increase of BALANCE is the problem
  • Current cost: 2149 gas, and hence would break after hard fork
  • Example TXs: 1, 2, 3

PayingProxy + GnosisSafe

  • Here a fixed amount of gas is forwarded. However, the gas amount is determined through an input variable.
  • Hence, this wouldn't break but users would have to realize that they have to adjust the gas amounts as current amounts in the example above would no longer work. Would be good to let know ahead of time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.