5

I'm assuming this has to do with Ethereum's 7 variable stack limit but in the following code test

  struct TestStruct5 {
    uint8 m_nOther1;
    uint8 m_nOther2;
    uint8 m_nOther3;
    uint8 m_nOther4;
    uint8 m_nOther5;
  }



struct TestStruct6 {
    uint8 m_nOther1;
    uint8 m_nOther2;
    uint8 m_nOther3;
    uint8 m_nOther4;
    uint8 m_nOther5;
    uint8 m_nOther6;
  }

  struct TestStruct7 {
    uint8 m_nOther1;
    uint8 m_nOther2;
    uint8 m_nOther3;
    uint8 m_nOther4;
    uint8 m_nOther5;
    uint8 m_nOther6;
    uint8 m_nOther7;
  }

  struct TestStruct8 {
    uint8 m_nOther1;
    uint8 m_nOther2;
    uint8 m_nOther3;
    uint8 m_nOther4;
    uint8 m_nOther5;
    uint8 m_nOther6;
    uint8 m_nOther7;
    uint8 m_nOther8;
  }



struct TestStruct9 {
    uint8 m_nOther1;
    uint8 m_nOther2;
    uint8 m_nOther3;
    uint8 m_nOther4;
    uint8 m_nOther5;
    uint8 m_nOther6;
    uint8 m_nOther7;
    uint8 m_nOther8;
    uint8 m_nOther9;
  }



TestStruct5[] internal m_testStruct5;
  TestStruct6[] internal m_testStruct6;
  TestStruct7[] internal m_testStruct7;
  TestStruct8[] internal m_testStruct8;
  TestStruct9[] internal m_testStruct9;



function GasTest5(address a_owner) external returns (uint256)
  {
    TestStruct5 memory item = TestStruct5({
      m_nOther1 : 0,
      m_nOther2 : 0,
      m_nOther3 : 0,
      m_nOther4 : 0,
      m_nOther5 : 0
    });
    uint256 nItemID = m_testStruct5.push(item) - 1;
  }



function GasTest6(address a_owner) external returns (uint256)
  {
    TestStruct6 memory item = TestStruct6({
      m_nOther1 : 0,
      m_nOther2 : 0,
      m_nOther3 : 0,
      m_nOther4 : 0,
      m_nOther5 : 0,
      m_nOther6 : 0
    });
    uint256 nItemID = m_testStruct6.push(item) - 1;
  }



function GasTest7(address a_owner) external returns (uint256)
  {
    TestStruct7 memory item = TestStruct7({
      m_nOther1 : 0,
      m_nOther2 : 0,
      m_nOther3 : 0,
      m_nOther4 : 0,
      m_nOther5 : 0,
      m_nOther6 : 0,
      m_nOther7 : 0
    });
    uint256 nItemID = m_testStruct7.push(item) - 1;
  }



function GasTest8(address a_owner) external returns (uint256)
  {
    TestStruct8 memory item = TestStruct8({
      m_nOther1 : 0,
      m_nOther2 : 0,
      m_nOther3 : 0,
      m_nOther4 : 0,
      m_nOther5 : 0,
      m_nOther6 : 0,
      m_nOther7 : 0,
      m_nOther8 : 0
    });
    uint256 nItemID = m_testStruct8.push(item) - 1;
  }



function GasTest9(address a_owner) external returns (uint256)
  {
    TestStruct9 memory item = TestStruct9({
      m_nOther1 : 0,
      m_nOther2 : 0,
      m_nOther3 : 0,
      m_nOther4 : 0,
      m_nOther5 : 0,
      m_nOther6 : 0,
      m_nOther7 : 0,
      m_nOther8 : 0,
      m_nOther9 : 0
    });
    uint256 nItemID = m_testStruct9.push(item) - 1;
  }

The Gas output for the following is:

GasTest5 gas: 49046
GasTest6 gas: 49098
GasTest7 gas: 49206
GasTest8 gas: 86443
GasTest9 gas: 91843

We can see that adding a 6th and 7th element to the struct adds trivial extra gas, but once you add an 8th item the gas cost almost doubles and a 9th item is 5k+ extra gas. Since all items are uint8s the total is well below a uint256.

What is causing this? I have a lot of data to track related to an ERC721 and was hoping to pack most of it into a single uint256 struct but this causes major problems.

Edit: Truffle Compiler settings

compilers: {
    solc: {
       version: "0.5.2",    // Fetch exact version from solc-bin (default: truffle's version)
      // docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)
       settings: {          // See the solidity docs for advice about optimization and evmVersion
        optimizer: {
          enabled: true,
          runs: 1000
        },
        //evmVersion: "constantinople"
      }
    }

Edit 2: It seems to have to do with pushing the item into an array or mapping. Without that gas doesnt explode with more struct items. I wonder if copying it into storage with more than 7 variables causes problems. No one seems to know the answer and I can't find any info anywhere.

  • 2
    Are you sure? I just tested it, and the transaction cost for GasTest9 is 48926 gas. What version of Solidity are you using and what compiler? – Eli Drion May 9 at 14:40
  • Added compiler settings to the post – John Snow May 9 at 21:56
  • I tried changing to 0.5.8 and get the same behavior – John Snow May 9 at 22:02
  • Are you using a 4.# compiler? – John Snow May 10 at 1:49
  • I am using solidity 0.5.2 – Eli Drion May 10 at 7:37
2

I have not tried in truffle but tested in remix Javascriot VM and Koban network.

Solidity Compiler version:0.5.8+commit.23d335f2.Emscripten.clang

Test results :

Ethereum Remix: Javascriot VM :

Gastest5 gas:  46317
GasTest6 gas:  46831
GasTest7 gas:  47389
GasTest8 gas:  48013
GasTest9 gas:  48616

Kovan Network :

First Call:

Gastest5 gas:  70317 
GasTest6 gas:  75631 
GasTest7 gas:  80989 
GasTest8 gas:  86413 
GasTest9 gas:  91816 

Second Call:

Gastest5 gas:  55317  
GasTest6 gas:  60631
GasTest7 gas:  65989
GasTest8 gas:  71413  
GasTest9 gas:  76816
  • Hmm. Possibly truffle gas estimator is wrong? – John Snow May 10 at 20:05
  • 1
    @JohnSnow I will test with later and update answer with truffle results. – Baracuda May 11 at 14:23
  • I'm still checking daily. Hoping to get some confirmed answers on this. It greatly effects my design for my contract. If I can only have 7 items I have to do my own bit packing which is annoying but workable. – John Snow May 12 at 3:20
  • Anything new? Has anyone done struct gas optimization tests that are out there somewhere? – John Snow May 14 at 2:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.