This is a follow-up question of Split Contract Design Pitfall

Background: I have split my logic into two contracts A and B. A keeps the state and not ETH. All state change functions are only allowed to come from its owner, contract B. Contract B functions interface with public (and are payable).

Everything works now. However, as business logic grows, the state contract A is literally on the edge of running out of gas during deployment -- i.e., at 8M gasLimit, it only deploys if solc optimizer runs <= 1000, any optimization higher than that will fail the deployment out-of-gas -- which means I can not add even a one-line function in contract A.

My question is: Is there a way to further split state contract while my state struct/mapping/array are pretty closely related to each other. Is there a design pattern that achieves separating states into multiple contracts yet still allow them to interact with each other?

How about the setter functions? Is there a way to separate the setter functions out yet allow them to set their state variable?

Or are there other smart ways that I'm not aware of?

  • 1
    I've used libraries to 'move' code out of the main contract. Another approach is to split also the storage in different contracts each managing its own part of the logic. – Ismael Jun 19 '19 at 20:36

It sounds to me like the state contract is too busy. Eternal Storage relies on storage contracts being as simple as possible. In that case, they would tend to not be very large. So, the size factor concerns me. It might imply an unwanted amount of complexity.

You can use libraries to avoid repetition.

Example, CRUD operations over key sets: https://github.com/rob-Hitchens/UnorderedKeySet

Referential integrity between related key sets: https://github.com/rob-Hitchens/LinkedSets

Ordered elements: https://github.com/rob-Hitchens/OrderStatisticsTree

There are many other ways to use libraries to create re-usable generalizations. Contracts that use them shed a lot of complexity. The bytecode is deployed separately, so libraries are a possible solution to the contract size limit.

Such an approach is generally better for code review as well, since the libraries can be considered separately and one can work on developing confidence that their behavior is predictable. Reliable, well-solved and well-understood components should, in theory, make it easier to reason about logic contracts that express the application. Especially when the app grows to a size that bumps up the gasLimit, it pays to enable code review.

Hope it helps.

| improve this answer | |
  • Hi Rob, thank you for your advice! I have been using your CRUD pattern since I read about it on Medium a year ago. Actually I'm already using it via an external library or else my state contract would be too big. Unfortunately that still leaves my state contract close to 1000 lines and "on the edge of running out of gas during deployment"... – Jerry Ji Jun 22 '19 at 16:24

I've used libraries to 'move' code out of my main contract. Libraries have its own set of limitations, they do not have direct access to contract storage (you need to pass a reference as parameter) and access (you have to define function as external, public/internal functions will be inlined).

Another approach is to split the logic in different contracts and have each contract manage its own part of storage. Using some type of white list to only allows your contracts to make some calls.

| improve this answer | |

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.