4

My contract is too large to deploy (gas cost) and I can't remove any functionality from it, so I need to split it up into two contracts that interact with each other. The first contract will be the only one the users interact with and the second one will throw any calls to it that are not from the first contract.

However, in my attempts at doing this, I've wrestled with how I should split up my variables and code between the contracts.

  • Q1: Is there a standard architecture/technique for doing this? How would you go about it?

Also, some questions:

  • Q2: Do mappings and arrays have automatic accessor functions? What about mixtures of mapping and array?If so, what is the syntax for these?

  • Q3: What are the gas concerns related to splitting up a contract in this way?

  • 3
    This might be hard to answer. Consider breaking this up into three or more precise questions. – Rob Hitchens - B9lab Feb 24 '17 at 22:58
3

For Q1: Assuming your contract is too large because you have complex data structures in it and therefore complex business logic to manipulate them, my approach has been to review the data model and consider some of these data structures to be contract themselves. For instance, your main contract holds a mapping of structure that contains array. If it is functionally relevant consider converting the structure into a contract and defining some business logic into this contract. In other words, try defining your object model by creating different contract for elements that have independent or different life cycles.

Q2: Rob already answered to this. I will just add that mapping and array automatic getter do not provide a length function. And mapping cannot be iterated over if you do not have already the list of keys.

Q3: here it depends if you can break your business logic to manipulate your data structure independently. From a gas perspective the more straightforward the VM process is the lower the gas will be. Also, with complex data structure one can assume searching in arrays, mapping, sorting and remapping that have a tendency of being loop intensive. So structure your internal data so you have a way to speed up the algorithms. For instance, i needed a sorted list of dates, i built it in a way that the inserting function keep it sorted. Another example is to delegate to the external caller more work to do, ie more separate transactions when possible. Update attributes of a contract in different calls versus one single call will all values.

Good luck and share your findings

1

Q1

I'm unsure there is widespread agreement about how to do this in all cases. I tend to incline toward separation of "data controllers" and "app controllers". Separating app logic from storage can be part of a larger strategy for enabling upgradeable logic. You can use libraries or send messages between contracts.

Q2

They have accessors if they're public. Here's a little toy that exposes the interfaces with Remix showing the arguments.

contract Accessors {
    address[] public customers;
    bytes32[] public invoices;
    mapping(address => bytes32[]) public customerInvoices;
    mapping(address => mapping(bytes32 => uint)) public customerInvoiceAmounts;
}

enter image description here

Q3

Both methods (Q1) imply packing, sending, receiving, and unpacking data that wouldn't occur if it was all internal.

Hope it helps.

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.