2

My smart-contract has gotten bigger than the 24K size allowed by Ethereum, so I had to break it up into smaller contracts. I then had to facilitate all sorts of inheritance and importing between these contracts to make everything work correctly again, but in the end, I was still unable to deploy successfully - and it seems to be for the same "contract-size-is-too-big" reason. Here’s what Truffle is telling me:

Error:  *** Deployment Failed ***

"DBTCFull" ran out of gas (using a value you set in your network config or deployment parameters.)
   * Block limit:  6721975 (0x6691b7)
   * Gas sent:     6721975 (0x6691b7)

I’m pretty sure I’m getting this error cause the contracts are all still too big.

Being that I'm only like 2/3 through this project and still have more code to write, size is going to be an issue. So what I’m really trying to understand here is what approach I should be taking. Should I use:

  1. Inheritance - where contracts import other contracts? or
  2. Reference - where contracts are deployed separately, then reference one another?

Here's a scenario that roughly illustrates how my contracts are supposed to interact with one another:

Contract # 1

contract MakePizza {
    // 1. This contract defines a “Pizza” Struct and…
    // 2. Has functions to make various Types of Pizza

    Struct Pizza { … }
   // etc.
}

Contract # 2

contract DeliverPizza is MakePizza {
    // This contract only handles the Deliveries of Pizzas. So it:
    // 1. Defines a “Customer” Struct (customer’s name, phone number, etc.)
    // 2. Defines a “CustomerAddress” Struct (street address, city, zip-code, etc.) 
    // 3. Has “delivery” type functions
}

Contract # 3

Contract HandleTheMoney is MakePizza, DeliverPizza  {
    // Defines all things having to do with funds:
    // 1. Acceptable Payment methods
    // 2. Defines various Fees
    // 3. Declares the Permissions re who’s allowed to withdraw funds from the contract, etc.
}

// And finally, Contract # 4:

contract JoesPizzaShop is MakePizza, DeliverPizza, HandleTheMoney {
    // The “master” contract, bringing everything together
}

So I’m pretty sure this is the Inheritance approach. (Cause I'm using that is MakePizza syntax here.)

Now here's the thing: this all compiles successfully - but fails to deploy, giving me the error I listed above. It seems importing all the files into one another isn't doing anything to reduce the overall size.

The other approach would be to deploy each contract separately, then get instances of them all in the “master” contract - the one called JoesPizzaShop - and work that way.

I’m thinking this latter approach would be “cleaner” - and hopefully smaller. But I worry it might also produce its own unique set of nightmares. Might even require major architectural changes.

So basically, I’m not sure how to proceed.

Given that the contract's size seems to be the main issue, which approach should I take?

Am I just executing the inheritance approach the wrong way - or is that just the wrong approach to begin with?

1 Answer 1

3

At compilation all contracts are combined together to produce one bytecode so inheritance will not be very helpful to decrease contract size.

From the error message the problem seems to be not enough gas and it appears you are using the default gas 6.7M gas. For reference at this moment mainnet has a block gas limit above 12M gas.

A temporary solution is increasing the block gas limit.

  • For truffle edit the network configuration and add gas parameter.

    networks: {
      development: {
        host: "127.0.0.1",
        port: 8545,
        network_id: "*",
        websockets: true,
        gas: 12000000          // <--- Increased block gas limit
      },
    
  • For ganache-cli pass requested gas to -l parameter:

      ganache-cli -l 12000000
    

An alternative to have separate contract is using libraries to separate functionality. It requires more planning to separate into libraries and adjusting the migrations scripts to deploy and link libraries into main contracts.

pragma solidity >=0.4.0 <0.8.0;
pragma experimental ABIEncoderV2;

library PizzaLib {
    struct Pizza {
        uint256 ingredients;
    }

    function mix(Pizza calldata pizza, uint256 ingredients) external pure returns (Pizza memory) {
        Pizza memory p = pizza;
        p.ingredients = ingredients;
        return p;
    }
}

contract PizzaShop {
    using PizzaLib for PizzaLib.Pizza;
    
    function foo() public pure returns (PizzaLib.Pizza memory) {
        PizzaLib.Pizza memory pizza;
        pizza = pizza.mix(23);
        return pizza;
    }
}

For example migrations will look like

module.exports = async function(deployer, network, accounts) {
  await deployer.deploy(PizzaLib);
  await deployer.link(PizzaLib, PizzaShop);
  await deployer.deploy(PizzaShop);
}

6
  • Wow that's a lot of good info! All sorts of really interesting options. Let me have a go at it and see where I land. I'll definitely respond with my results and findings. Thank you!
    – Mark55
    Commented Dec 5, 2020 at 5:03
  • 1
    Hey Ismael, I tackled the simplest thing first which - as per your suggestion - was to raise the gas to 12000000 in truffle-config.js. But that gave me an error about exceeding the Block-gas-limit, which is hard-coded by Ganache to 6721975. So the two conflicted. Since Ganache doesn't let you set gas manually, I ditched it for ganache-cli, manually set the gas there - and that solved the problem. Next I'll look into converting some of my code to libraries as I've also come across several articles that recommend this. So thanks again - marking yours as the right answer.
    – Mark55
    Commented Dec 6, 2020 at 2:39
  • hey quick follow-up question: I understand that the migrations script you posted will be doing the linking of PizzaLib to the PizzaShop smart-contract. And so I gather this means I'll have to then take the explicit import "./PizzaLib.sol"; statement out of the PizzaShop smart-contract, right? But then I still need to somehow reference and instantiate PizzaLib in the PizzaShop contract so I can use it, correct? If so, how? ALSO, do I still put the using PizzaLib for PizzaLib.Pizza; statement in the PizzaShop contract?
    – Mark55
    Commented Dec 12, 2020 at 19:52
  • @Mark55 If PizzaLib is in a separate file you will need to import since it is required by the compiler. When calling an external function from a library the compiler leaves a placeholde in the bytecode for the library address. The link command in the migrations scripts replaces the placeholder with the deployed library address. You don't have to instantiate the library everything was resolved by compiler + link. If you have more doubts about libraries it is better to search the site and create a new question if you were unable to find a suitable answer.
    – Ismael
    Commented Dec 12, 2020 at 22:46
  • Sounds good - I'm gonna do some tests here on my end and you're right: perhaps I'll also post a new separate question regarding my follow-up Q's. Thanks!
    – Mark55
    Commented Dec 13, 2020 at 19:22

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.